home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.0 / stk-3 / blt-for-STk-3.0 / blt-1.9 / src / bltGrAxis.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-26  |  73.4 KB  |  2,384 lines

  1. /*
  2.  * bltGrAxis.c --
  3.  *
  4.  *    This module implements coordinate axes for a graph
  5.  *    widget in the Tk toolkit.
  6.  *
  7.  * Copyright 1991-1994 by AT&T Bell Laboratories.
  8.  * Permission to use, copy, modify, and distribute this software
  9.  * and its documentation for any purpose and without fee is hereby
  10.  * granted, provided that the above copyright notice appear in all
  11.  * copies and that both that the copyright notice and warranty
  12.  * disclaimer appear in supporting documentation, and that the
  13.  * names of AT&T Bell Laboratories any of their entities not be used
  14.  * in advertising or publicity pertaining to distribution of the
  15.  * software without specific, written prior permission.
  16.  *
  17.  * AT&T disclaims all warranties with regard to this software, including
  18.  * all implied warranties of merchantability and fitness.  In no event
  19.  * shall AT&T be liable for any special, indirect or consequential
  20.  * damages or any damages whatsoever resulting from loss of use, data
  21.  * or profits, whether in an action of contract, negligence or other
  22.  * tortuous action, arising out of or in connection with the use or
  23.  * performance of this software.
  24.  *
  25.  */
  26.  
  27. #include <assert.h>
  28. #include "blt.h"
  29. #include "bltGraph.h"
  30. #include "bltGrElem.h"
  31. #include <ctype.h>
  32. #include <X11/Xutil.h>
  33. #include <X11/Xatom.h>
  34.  
  35. #define NTICK 5
  36.  
  37. #ifndef SHRT_MIN
  38. #define SHRT_MIN                -0x8000
  39. #endif /* SHRT_MIN */
  40. #ifndef SHRT_MAX
  41. #define SHRT_MAX                 0x7FFF
  42. #endif /* SHRT_MAX */
  43.  
  44. /*
  45.  * Round x in terms of units
  46.  */
  47. #define UROUND(x,u)    (BLT_RND((x)/(u))*(u))
  48. #define UCEIL(x,u)    (ceil((x)/(u))*(u))
  49. #define UFLOOR(x,u)    (floor((x)/(u))*(u))
  50.  
  51. #define DEF_MAJOR_TICK     0.030    /* Length of a major tick */
  52. #define DEF_MINOR_TICK     0.015    /* Length of a minor (sub)tick */
  53. #define DEF_LABEL_TICK     0.040    /* Distance from graph to start of label */
  54.  
  55. #define NUMDIGITS    15    /* Specifies the number of digits of
  56.                  * accuracy used when outputting axis
  57.                  * tick labels. */
  58. enum PositionIndices {
  59.     MAJOR_TICK, MINOR_TICK, TICK_LABEL, AXIS_LINE
  60. };
  61.  
  62. #define AVG_TICK_NUM_CHARS    16    /* Assumed average tick label size */
  63. #define LABEL_MAX_SIZE        127    /* New buffer size to avoid crashes */
  64.  
  65. enum LimitIndices {
  66.     LMIN, LMAX
  67. };
  68.  
  69. /* Note: The following array is ordered according to enum AxisTypes */
  70. static char *axisNames[] =
  71. {
  72.     "x", "y", "x2", "y2"
  73. };
  74.  
  75. /* Map normalized coordinates to window coordinates */
  76. #define MAPX(X,x)        (BLT_RND((x)*(X)->scale)+(X)->offset)
  77. #define MAPY(Y,y)        ((Y)->offset-BLT_RND((y)*(Y)->scale))
  78.  
  79. /* Map graph coordinates to normalized coordinates [0..1] */
  80. #define NORM(a,x)     (((x) - (a)->min) / (a)->range)
  81.  
  82. /*
  83.  * Sun's bundled and unbundled C compilers choke on static function
  84.  * typedefs (while it can handle extern declarations) like
  85.  *
  86.  *     static Tk_OptionParseProc parseProc;
  87.  *      static Tk_OptionPrintProc printProc;
  88.  *
  89.  * As a workaround, provide forward declarations here:
  90.  */
  91. static int ParseAxisLimit _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *value, char *widgRec, int offset));
  92. static char *PrintAxisLimit _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
  93.  
  94. static Tk_CustomOption MinLimitOption =
  95. {
  96.     ParseAxisLimit, PrintAxisLimit, (ClientData)LMIN,
  97. };
  98. static Tk_CustomOption MaxLimitOption =
  99. {
  100.     ParseAxisLimit, PrintAxisLimit, (ClientData)LMAX,
  101. };
  102.  
  103. /*
  104.  * ----------------------------------------------------------------------
  105.  *
  106.  * Label --
  107.  *
  108.  *     Structure containing the formatted label and the screen
  109.  *     coordinates position of the tick label (anchored at its
  110.  *     center).
  111.  *
  112.  * ----------------------------------------------------------------------
  113.  */
  114. typedef struct {
  115.     char *text;            /* Label for tick on axis */
  116.     short int x, y;        /* Window position of tick on graph */
  117. } Label;
  118.  
  119. /*
  120.  * ----------------------------------------------------------------------
  121.  *
  122.  * Axis --
  123.  *
  124.  *     Structure contains options controlling how the axis will be
  125.  *     displayed.
  126.  *
  127.  * ----------------------------------------------------------------------
  128.  */
  129.  
  130. typedef struct {
  131.     enum AxisTypes type;    /* Type of axis: X1, Y1, X2, or Y2 */
  132.     enum AxisLocations location;/* Location of the axis relative to plotting
  133.                  * surface: right, left, etc. */
  134.     int logScale;        /* If non-zero, scale values logarithmically */
  135.     int mapped;            /* If non-zero, display the axis */
  136.  
  137.     AxisDisplayProc *displayProc;
  138.     AxisPrintProc *printProc;
  139.     AxisLayoutProc *layoutProc;
  140.     AxisDestroyProc *destroyProc;
  141.  
  142.     /* User-definable fields */
  143.     char *title;        /* Axis title */
  144.     int showTicks;        /* If non-zero, display (both major
  145.                  * and minor) ticks on the axis */
  146.     int loose;            /* If non-zero, autoscale limits loosely */
  147.     int descending;        /* In non-zero, axis values are descending
  148.                  * instead of monotonically increasing. */
  149.     double limits[2];        /* Limits for scaling of axis */
  150.     double prevMin, prevMax;    /* Last limits for scaling of axis */
  151.     double reqStep;        /* Manually selected step size for
  152.                  * major ticks: If zero or less,
  153.                  * automatically calculate a "best"
  154.                  * step size based on range of
  155.                  * values. */
  156.     int reqSubTicks;        /* Manually selected # of subticks:
  157.                  * The default value is 2. */
  158.     XFontStruct *fontPtr;    /* Font used to draw tick labels. */
  159.     XColor *fgColorPtr;        /* Foreground color for ticks, labels,
  160.                  * and axis */
  161.     int lineWidth;        /* Line thickness of axis and ticks */
  162.     double theta;        /* Rotation of tick labels in degrees. */
  163.     char *formatCmd;        /* If non-NULL, indicates a Tcl proc
  164.                  * to call when formatting tick
  165.                  * labels. See the manual for its
  166.                  * usage. */
  167.     double subStep;        /* Step interval between minor ticks */
  168.     int subTicks;        /* # of minor ticks between major
  169.                  * ticks */
  170.     double step;        /* Step interval between major ticks */
  171.     int numTicks;        /* # of major ticks possible on axis:
  172.                  * Calculated by tick layout routines. */
  173.  
  174.     /* Calculated values */
  175.     unsigned int flags;
  176.  
  177.     Tk_Anchor anchor;        /* Anchor of tick labels */
  178.     int posArr[4];        /* Screen location of axis, major tick,
  179.                  * minor tick, and tick label */
  180.     XPoint titlePos;        /* Coordinates of axis title */
  181.  
  182.     Tcl_Interp *interp;
  183.     unsigned int width, height;    /* Bounding box of axis */
  184.  
  185.     double tickMin, tickMax;    /* Smallest and largest possible major
  186.                  * ticks on the plot */
  187.     int tickLength;        /* Legend of major tick on axis. */
  188.     double min, max;        /* Actual (including padding) axis limits */
  189.     double range;        /* Range of values (max-min) */
  190.     double scale;        /* Scale factor to convert values to
  191.                  * pixels */
  192.     int offset;            /* Offset of plotting region from window
  193.                  * origin */
  194.  
  195.     GC lineGC;            /* Graph context for axis lines and ticks */
  196.     GC textGC;            /* Graphic context for tick labels:
  197.                  * Must be a private GC (can't use
  198.                  * Tk_GetGC) because the FillStyle,
  199.                  * TSOrigin, and Stipple fields may
  200.                  * change when the graph is layout is
  201.                  * calculated, to accommodate rotation
  202.                  * of text. Also, note that the
  203.                  * background color is also reset when
  204.                  * the background color of the graph
  205.                  * changes. */
  206.     int numSegments;        /* Size of the above segment array */
  207.     XSegment *segArr;        /* Array of computed tick line
  208.                  * segments. Also includes the axis
  209.                  * line */
  210.     int numLabels;        /* Size of the above label array */
  211.     Label *labelArr;        /* Array of computed tick labels: See the
  212.                  * description of the Label structure. */
  213.  
  214. } Axis;
  215.  
  216. /* Axis flags: */
  217. #define AXIS_CONFIG_DIRTY    (1<<8)
  218. #define AXIS_CONFIG_USER_BIT    (1<<9)
  219. #define AXIS_CONFIG_MIN_MASK    (AXIS_CONFIG_USER_BIT << LMIN)
  220. #define AXIS_CONFIG_MAX_MASK    (AXIS_CONFIG_USER_BIT << LMAX)
  221. #define AXIS_CONFIG_MIN_SET(a)     ((a)->flags & AXIS_CONFIG_MIN_MASK)
  222. #define AXIS_CONFIG_MAX_SET(a)     ((a)->flags & AXIS_CONFIG_MAX_MASK)
  223.  
  224. #define VERTICAL_AXIS(a)    ((a)->location&1)
  225. #define HORIZONTAL_AXIS(a)        (!((a)->location&1))
  226.  
  227. #define DEF_AXIS_ALT_MAPPED     "false"
  228. #define DEF_AXIS_COMMAND    (char *)NULL
  229. #define DEF_AXIS_DESCENDING     "0"
  230. #define DEF_AXIS_FG_COLOR    BLACK
  231. #define DEF_AXIS_FG_MONO    BLACK
  232. #define DEF_AXIS_FONT         "-*-Courier-Medium-R-Normal--*-100-*-*-*-*-*-*"
  233. #define DEF_AXIS_TICK_LENGTH    "0.1i"
  234. #define DEF_AXIS_LINE_WIDTH    "0"
  235. #define DEF_AXIS_LOG_SCALE     "0"
  236. #define DEF_AXIS_LOOSE         "0"
  237. #define DEF_AXIS_MAX        (char *)NULL
  238. #define DEF_AXIS_MIN        (char *)NULL
  239. #define DEF_AXIS_ROTATE        "0.0"
  240. #define DEF_AXIS_STD_MAPPED     "1"
  241. #define DEF_AXIS_STEPSIZE    "0.0"
  242. #define DEF_AXIS_SUBTICKS    "2"
  243. #define DEF_AXIS_TICKS        "1"
  244. #define DEF_AXIS_X_STEPSIZE_BARCHART    "1.0"
  245. #define DEF_AXIS_X_SUBTICKS_BARCHART    "0"
  246. #define DEF_AXIS_TITLE        (char *)NULL
  247.  
  248. static Tk_ConfigSpec xAxisConfigSpecs[] =
  249. {
  250.     {TK_CONFIG_COLOR, "-color", "xColor", "AxisColor",
  251.     DEF_AXIS_FG_COLOR, Tk_Offset(Axis, fgColorPtr),
  252.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  253.     {TK_CONFIG_COLOR, "-color", "xColor", "AxisColor",
  254.     DEF_AXIS_FG_MONO, Tk_Offset(Axis, fgColorPtr),
  255.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  256.     {TK_CONFIG_STRING, "-command", "xCommand", "AxisCommand",
  257.     DEF_AXIS_COMMAND, Tk_Offset(Axis, formatCmd),
  258.     ALL_MASK | TK_CONFIG_NULL_OK},
  259.     {TK_CONFIG_BOOLEAN, "-descending", "xDescending", "AxisDescending",
  260.     DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
  261.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  262.     {TK_CONFIG_FONT, "-font", "xFont", "AxisFont",
  263.     DEF_AXIS_FONT, Tk_Offset(Axis, fontPtr), ALL_MASK},
  264.     {TK_CONFIG_PIXELS, "-linewidth", "xLinewidth", "AxisLinewidth",
  265.     DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
  266.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  267.     {TK_CONFIG_BOOLEAN, "-logscale", "xLogscale", "AxisLogscale",
  268.     DEF_AXIS_LOG_SCALE, Tk_Offset(Axis, logScale),
  269.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  270.     {TK_CONFIG_BOOLEAN, "-loose", "xLoose", "AxisLoose",
  271.     DEF_AXIS_LOOSE, Tk_Offset(Axis, loose),
  272.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  273.     {TK_CONFIG_BOOLEAN, "-mapped", "xMapped", "AxisMapped",
  274.     DEF_AXIS_STD_MAPPED, Tk_Offset(Axis, mapped),
  275.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  276.     {TK_CONFIG_CUSTOM, "-max", "xMax", "AxisMax",
  277.     DEF_AXIS_MAX, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption},
  278.     {TK_CONFIG_CUSTOM, "-min", "xMin", "AxisMin",
  279.     DEF_AXIS_MIN, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption},
  280.     {TK_CONFIG_DOUBLE, "-rotate", "xRotate", "AxisRotate",
  281.     DEF_AXIS_ROTATE, Tk_Offset(Axis, theta),
  282.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  283.     {TK_CONFIG_BOOLEAN, "-showticks", "xShowticks", "AxisShowticks",
  284.     DEF_AXIS_TICKS, Tk_Offset(Axis, showTicks),
  285.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  286.     {TK_CONFIG_DOUBLE, "-stepsize", "xStepsize", "AxisStepsize",
  287.     DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep), XYGRAPH_MASK},
  288.     {TK_CONFIG_DOUBLE, "-stepsize", "xStepsize", "AxisStepsize",
  289.     DEF_AXIS_X_STEPSIZE_BARCHART, Tk_Offset(Axis, reqStep),
  290.     BARCHART_MASK},
  291.     {TK_CONFIG_INT, "-subticks", "xSubticks", "AxisSubticks",
  292.     DEF_AXIS_SUBTICKS, Tk_Offset(Axis, reqSubTicks), XYGRAPH_MASK},
  293.     {TK_CONFIG_INT, "-subticks", "xSubticks", "AxisSubticks",
  294.     DEF_AXIS_X_SUBTICKS_BARCHART, Tk_Offset(Axis, reqSubTicks),
  295.     BARCHART_MASK},
  296.     {TK_CONFIG_PIXELS, "-ticklength", "xTickLength", "AxisTickLength",
  297.     DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_MASK},
  298.     {TK_CONFIG_STRING, "-title", "xTitle", "AxisTitle",
  299.     "X", Tk_Offset(Axis, title), ALL_MASK | TK_CONFIG_NULL_OK},
  300.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  301. };
  302.  
  303.  
  304. static Tk_ConfigSpec x2AxisConfigSpecs[] =
  305. {
  306.     {TK_CONFIG_COLOR, "-color", "x2Color", "AxisColor",
  307.     DEF_AXIS_FG_COLOR, Tk_Offset(Axis, fgColorPtr),
  308.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  309.     {TK_CONFIG_COLOR, "-color", "x2Color", "AxisColor",
  310.     DEF_AXIS_FG_MONO, Tk_Offset(Axis, fgColorPtr),
  311.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  312.     {TK_CONFIG_STRING, "-command", "x2Command", "AxisCommand",
  313.     DEF_AXIS_COMMAND, Tk_Offset(Axis, formatCmd),
  314.     ALL_MASK | TK_CONFIG_NULL_OK},
  315.     {TK_CONFIG_BOOLEAN, "-descending", "x2Descending", "AxisDescending",
  316.     DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
  317.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  318.     {TK_CONFIG_FONT, "-font", "x2Font", "AxisFont",
  319.     DEF_AXIS_FONT, Tk_Offset(Axis, fontPtr), ALL_MASK},
  320.     {TK_CONFIG_PIXELS, "-linewidth", "x2Linewidth", "AxisLinewidth",
  321.     DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
  322.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  323.     {TK_CONFIG_BOOLEAN, "-logscale", "x2Logscale", "AxisLogscale",
  324.     DEF_AXIS_LOG_SCALE, Tk_Offset(Axis, logScale),
  325.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  326.     {TK_CONFIG_BOOLEAN, "-loose", "x2Loose", "AxisLoose",
  327.     DEF_AXIS_LOOSE, Tk_Offset(Axis, loose),
  328.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  329.     {TK_CONFIG_BOOLEAN, "-mapped", "x2Mapped", "AxisMapped",
  330.     DEF_AXIS_ALT_MAPPED, Tk_Offset(Axis, mapped),
  331.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  332.     {TK_CONFIG_CUSTOM, "-max", "x2Max", "AxisMax",
  333.     DEF_AXIS_MAX, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption},
  334.     {TK_CONFIG_CUSTOM, "-min", "x2Min", "AxisMin",
  335.     DEF_AXIS_MIN, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption},
  336.     {TK_CONFIG_DOUBLE, "-rotate", "x2Rotate", "AxisRotate",
  337.     DEF_AXIS_ROTATE, Tk_Offset(Axis, theta),
  338.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  339.     {TK_CONFIG_BOOLEAN, "-showticks", "x2Showticks", "AxisShowticks",
  340.     DEF_AXIS_TICKS, Tk_Offset(Axis, showTicks), ALL_MASK},
  341.     {TK_CONFIG_DOUBLE, "-stepsize", "x2Stepsize", "AxisStepsize",
  342.     DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep),
  343.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  344.     {TK_CONFIG_INT, "-subticks", "x2Subticks", "AxisSubticks",
  345.     DEF_AXIS_SUBTICKS, Tk_Offset(Axis, reqSubTicks),
  346.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  347.     {TK_CONFIG_PIXELS, "-ticklength", "x2TickLength", "AxisTickLength",
  348.     DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_MASK},
  349.     {TK_CONFIG_STRING, "-title", "x2Title", "AxisTitle",
  350.     DEF_AXIS_TITLE, Tk_Offset(Axis, title), ALL_MASK | TK_CONFIG_NULL_OK},
  351.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  352. };
  353.  
  354. static Tk_ConfigSpec yAxisConfigSpecs[] =
  355. {
  356.     {TK_CONFIG_COLOR, "-color", "yColor", "AxisColor",
  357.     DEF_AXIS_FG_COLOR, Tk_Offset(Axis, fgColorPtr),
  358.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  359.     {TK_CONFIG_COLOR, "-color", "yColor", "AxisColor",
  360.     DEF_AXIS_FG_MONO, Tk_Offset(Axis, fgColorPtr),
  361.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  362.     {TK_CONFIG_STRING, "-command", "yCommand", "AxisCommand",
  363.     (char *)NULL, Tk_Offset(Axis, formatCmd),
  364.     ALL_MASK | TK_CONFIG_NULL_OK},
  365.     {TK_CONFIG_BOOLEAN, "-descending", "yDescending", "AxisDescending",
  366.     DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
  367.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  368.     {TK_CONFIG_FONT, "-font", "yFont", "AxisFont",
  369.     DEF_AXIS_FONT, Tk_Offset(Axis, fontPtr), ALL_MASK},
  370.     {TK_CONFIG_BOOLEAN, "-loose", "yLoose", "AxisLoose",
  371.     DEF_AXIS_LOOSE, Tk_Offset(Axis, loose),
  372.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  373.     {TK_CONFIG_PIXELS, "-linewidth", "yLinewidth", "AxisLinewidth",
  374.     DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
  375.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  376.     {TK_CONFIG_BOOLEAN, "-logscale", "yLogscale", "AxisLogscale",
  377.     DEF_AXIS_LOG_SCALE, Tk_Offset(Axis, logScale),
  378.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  379.     {TK_CONFIG_BOOLEAN, "-mapped", "yMapped", "AxisMapped",
  380.     DEF_AXIS_STD_MAPPED, Tk_Offset(Axis, mapped),
  381.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  382.     {TK_CONFIG_CUSTOM, "-max", "yMax", "AxisMax",
  383.     DEF_AXIS_MAX, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption},
  384.     {TK_CONFIG_CUSTOM, "-min", "yMin", "AxisMin",
  385.     DEF_AXIS_MIN, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption},
  386.     {TK_CONFIG_DOUBLE, "-rotate", "yRotate", "AxisRotate",
  387.     DEF_AXIS_ROTATE, Tk_Offset(Axis, theta),
  388.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  389.     {TK_CONFIG_BOOLEAN, "-showticks", "yShowticks", "AxisShowticks",
  390.     DEF_AXIS_TICKS, Tk_Offset(Axis, showTicks),
  391.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  392.     {TK_CONFIG_DOUBLE, "-stepsize", "yStepsize", "AxisStepsize",
  393.     DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep),
  394.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  395.     {TK_CONFIG_INT, "-subticks", "ySubticks", "AxisSubticks",
  396.     DEF_AXIS_SUBTICKS, Tk_Offset(Axis, reqSubTicks), ALL_MASK},
  397.     {TK_CONFIG_PIXELS, "-ticklength", "yTickLength", "AxisTickLength",
  398.     DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_MASK},
  399.     {TK_CONFIG_STRING, "-title", "yTitle", "AxisTitle",
  400.     "Y", Tk_Offset(Axis, title), ALL_MASK | TK_CONFIG_NULL_OK},
  401.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  402. };
  403.  
  404. static Tk_ConfigSpec y2AxisConfigSpecs[] =
  405. {
  406.     {TK_CONFIG_COLOR, "-color", "y2Color", "AxisColor",
  407.     DEF_AXIS_FG_COLOR, Tk_Offset(Axis, fgColorPtr),
  408.     TK_CONFIG_COLOR_ONLY | ALL_MASK},
  409.     {TK_CONFIG_COLOR, "-color", "y2Color", "AxisColor",
  410.     DEF_AXIS_FG_MONO, Tk_Offset(Axis, fgColorPtr),
  411.     TK_CONFIG_MONO_ONLY | ALL_MASK},
  412.     {TK_CONFIG_STRING, "-command", "y2Command", "AxisCommand",
  413.     DEF_AXIS_COMMAND, Tk_Offset(Axis, formatCmd),
  414.     ALL_MASK | TK_CONFIG_NULL_OK},
  415.     {TK_CONFIG_BOOLEAN, "-descending", "y2Descending", "AxisDescending",
  416.     DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
  417.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  418.     {TK_CONFIG_FONT, "-font", "y2Font", "AxisFont",
  419.     DEF_AXIS_FONT, Tk_Offset(Axis, fontPtr), ALL_MASK},
  420.     {TK_CONFIG_PIXELS, "-linewidth", "y2Linewidth", "AxisLinewidth",
  421.     DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
  422.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  423.     {TK_CONFIG_BOOLEAN, "-loose", "y2Loose", "AxisLoose",
  424.     DEF_AXIS_LOOSE, Tk_Offset(Axis, loose),
  425.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  426.     {TK_CONFIG_BOOLEAN, "-logscale", "y2Logscale", "AxisLogscale",
  427.     DEF_AXIS_LOG_SCALE, Tk_Offset(Axis, logScale),
  428.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  429.     {TK_CONFIG_BOOLEAN, "-mapped", "y2Mapped", "AxisMapped",
  430.     DEF_AXIS_ALT_MAPPED, Tk_Offset(Axis, mapped),
  431.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  432.     {TK_CONFIG_CUSTOM, "-max", "y2Max", "AxisMax",
  433.     DEF_AXIS_MAX, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption},
  434.     {TK_CONFIG_CUSTOM, "-min", "y2Min", "AxisMin",
  435.     DEF_AXIS_MIN, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption},
  436.     {TK_CONFIG_DOUBLE, "-rotate", "y2Rotate", "AxisRotate",
  437.     DEF_AXIS_ROTATE, Tk_Offset(Axis, theta),
  438.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  439.     {TK_CONFIG_BOOLEAN, "-showticks", "y2Showticks", "AxisShowticks",
  440.     DEF_AXIS_TICKS, Tk_Offset(Axis, showTicks),
  441.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  442.     {TK_CONFIG_DOUBLE, "-stepsize", "y2Stepsize", "AxisStepsize",
  443.     DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep),
  444.     TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
  445.     {TK_CONFIG_INT, "-subticks", "y2Subticks", "AxisSubticks",
  446.     DEF_AXIS_SUBTICKS, Tk_Offset(Axis, reqSubTicks), ALL_MASK},
  447.     {TK_CONFIG_PIXELS, "-ticklength", "y2TickLength", "AxisTickLength",
  448.     DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_MASK},
  449.     {TK_CONFIG_STRING, "-title", "y2Title", "AxisTitle",
  450.     DEF_AXIS_TITLE, Tk_Offset(Axis, title), ALL_MASK | TK_CONFIG_NULL_OK},
  451.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  452. };
  453.  
  454. static Tk_ConfigSpec *axisConfigSpecs[4] =
  455. {
  456.     xAxisConfigSpecs, yAxisConfigSpecs,    /* Normal x-axis and y-axis */
  457.     x2AxisConfigSpecs, y2AxisConfigSpecs    /* Alternate x-axis and y-axis */
  458. };
  459.  
  460. /* ----------------------------------------------------------------------
  461.  * Custom option parse and print procedures
  462.  * ----------------------------------------------------------------------
  463.  */
  464. /*
  465.  * ----------------------------------------------------------------------
  466.  *
  467.  * ParseAxisLimit --
  468.  *
  469.  *    Convert the string representation of an axis limit into its
  470.  *    numeric form.
  471.  *
  472.  * Results:
  473.  *    The return value is a standard Tcl result.  The symbol type is
  474.  *    written into the widget record.
  475.  *
  476.  * ----------------------------------------------------------------------
  477.  */
  478. /*ARGSUSED*/
  479. static int
  480. ParseAxisLimit(clientData, interp, tkwin, value, widgRec, offset)
  481.     ClientData clientData;    /* Either LMIN or LMAX */
  482.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  483.     Tk_Window tkwin;        /* not used */
  484.     char *value;        /* */
  485.     char *widgRec;        /* Axis structure */
  486.     int offset;            /* Offset of limit */
  487. {
  488.     Axis *axisPtr = (Axis *)(widgRec);
  489.     int whichLimit = (int)clientData;
  490.     unsigned int mask;
  491.  
  492.     mask = (AXIS_CONFIG_USER_BIT << whichLimit);
  493.     if ((value == NULL) || (*value == '\0')) {
  494.     axisPtr->flags &= ~mask;
  495.     } else {
  496.     double newLimit;
  497.  
  498.     if (Tcl_ExprDouble(interp, value, &newLimit) != TCL_OK) {
  499.         return TCL_ERROR;
  500.     }
  501.     axisPtr->limits[whichLimit] = newLimit;
  502.     axisPtr->flags |= mask;
  503.     }
  504.     return TCL_OK;
  505. }
  506.  
  507. /*
  508.  * ----------------------------------------------------------------------
  509.  *
  510.  * PrintAxisLimit --
  511.  *
  512.  *    Convert the floating point axis limit into a string.
  513.  *
  514.  * Results:
  515.  *    The string representation of the limit is returned.
  516.  *
  517.  * ----------------------------------------------------------------------
  518.  */
  519. /*ARGSUSED*/
  520. static char *
  521. PrintAxisLimit(clientData, tkwin, widgRec, offset, freeProcPtr)
  522.     ClientData clientData;    /* Either LMIN or LMAX */
  523.     Tk_Window tkwin;        /* not used */
  524.     char *widgRec;        /* */
  525.     int offset;
  526.     Tcl_FreeProc **freeProcPtr;
  527. {
  528.     Axis *axisPtr = (Axis *)(widgRec);
  529.     int whichLimit = (int)clientData;
  530.     unsigned int mask;
  531.     char *result;
  532.  
  533.     result = "";
  534.     mask = (AXIS_CONFIG_USER_BIT << whichLimit);
  535.     if (axisPtr->flags & mask) {
  536.     char string[TCL_DOUBLE_SPACE + 1];
  537.  
  538.     Tcl_PrintDouble(axisPtr->interp, axisPtr->limits[whichLimit],
  539.         string);
  540.     result = strdup(string);
  541.     if (result == NULL) {
  542.         return "";
  543.     }
  544.     *freeProcPtr = (Tcl_FreeProc *)free;
  545.     }
  546.     return result;
  547. }
  548.  
  549. /*
  550.  * ----------------------------------------------------------------------
  551.  *
  552.  * MakeLabel --
  553.  *
  554.  *    Converts a floating point tick value to a string representation.
  555.  *
  556.  * Results:
  557.  *    Returns a formatted label in the string buffer.
  558.  *
  559.  * Side Effects:
  560.  *    Formatted tick label will be displayed on the graph.
  561.  *
  562.  * ----------------------------------------------------------------------
  563.  */
  564. static int
  565. MakeLabel(graphPtr, axisPtr, value, string)
  566.     Graph *graphPtr;        /* Graph widget */
  567.     Axis *axisPtr;        /* Axis structure */
  568.     double value;        /* */
  569.     char string[];        /* string (length is always
  570.                  * LABEL_MAX_SIZE+1) containing the
  571.                  * formatted label */
  572. {
  573.     if (axisPtr->logScale) {
  574.     sprintf(string, "1E%d", BLT_RND(value));
  575.     } else {
  576.     if (axisPtr->formatCmd == NULL) {
  577.         sprintf(string, "%.*g", NUMDIGITS, value);
  578.     } else {
  579.         Tcl_PrintDouble(axisPtr->interp, value, string);
  580.     }
  581.     }
  582.     if (axisPtr->formatCmd != NULL) {
  583.     Tcl_ResetResult(axisPtr->interp);
  584.     if (Tcl_VarEval(axisPtr->interp, axisPtr->formatCmd, " ",
  585.         Tk_PathName(graphPtr->tkwin), " ",
  586.         string, (char *)NULL) != TCL_OK) {
  587.         Tk_BackgroundError(axisPtr->interp);
  588.     } else {
  589.         char *result;
  590.  
  591.         result = axisPtr->interp->result;
  592.         if (*result != '\0') {
  593.         strncpy(string, result, LABEL_MAX_SIZE);
  594.         string[LABEL_MAX_SIZE] = 0;
  595.         Tcl_ResetResult(axisPtr->interp);
  596.         }
  597.     }
  598.     }
  599.     return TCL_OK;
  600. }
  601.  
  602. /* Map graph coordinate to normalized coordinates (consider log scale) */
  603. static double
  604. Scale(axisPtr, x)
  605.     Axis *axisPtr;
  606.     double x;
  607. {
  608.     if (x == Blt_posInfinity) {
  609.     return (1.0);
  610.     } else if (x == Blt_negInfinity) {
  611.     return (0.0);
  612.     }
  613.     if (axisPtr->logScale) {
  614.     if (x > 0.0) {
  615.         x = log10(x);
  616.     } else if (x < 0.0) {
  617.         x = 0.0;
  618.     }
  619.     }
  620.     return (NORM(axisPtr, x));
  621. }
  622.  
  623. /*
  624.  * ----------------------------------------------------------------------
  625.  *
  626.  * Blt_InvTransform --
  627.  *
  628.  *    Maps the given window y-coordinate back to a graph coordinate
  629.  *    value. Called by the graph locater routine.
  630.  *
  631.  * Results:
  632.  *    Returns the graph coordinate value at the given window
  633.  *    y-coordinate.
  634.  *
  635.  * ----------------------------------------------------------------------
  636.  */
  637. double
  638. Blt_InvTransform(axis, coord)
  639.     GraphAxis *axis;
  640.     int coord;
  641. {
  642.     double norm, value;
  643.     Axis *axisPtr = (Axis *)axis;
  644.  
  645.     if (HORIZONTAL_AXIS(axisPtr)) {
  646.     coord = (coord - axisPtr->offset);
  647.     } else {
  648.     coord = (axisPtr->offset - coord);
  649.     }
  650.     norm = ((double)coord / axisPtr->scale);
  651.     if (axisPtr->descending) {
  652.     norm = 1.0 - norm;
  653.     }
  654.     value = (norm * axisPtr->range) + axisPtr->min;
  655.     if (axisPtr->logScale) {
  656.     value = BLT_EXP10(value);
  657.     }
  658.     return (value);
  659. }
  660.  
  661. /*
  662.  * ----------------------------------------------------------------------
  663.  *
  664.  * Blt_Transform --
  665.  *
  666.  *    Map the given graph coordinate value to its axis, returning a
  667.  *    window position.
  668.  *
  669.  * Results:
  670.  *    Returns the window coordinate position on the given axis.
  671.  *
  672.  * Note:
  673.  *    Since line and polygon clipping is performed by the X server,
  674.  *    we must be careful about coordinates which are outside of the
  675.  *      range of a signed short int.
  676.  *
  677.  * ----------------------------------------------------------------------
  678.  */
  679. int
  680. Blt_Transform(axis, value)
  681.     GraphAxis *axis;
  682.     double value;
  683. {
  684.     Axis *axisPtr = (Axis *)axis;
  685.     double norm;
  686.     int coord;
  687.  
  688.     norm = Scale(axisPtr, value);
  689.     if (axisPtr->descending) {
  690.     norm = 1.0 - norm;
  691.     }
  692.     coord = HORIZONTAL_AXIS(axisPtr)
  693.     ? MAPX(axisPtr, norm) : MAPY(axisPtr, norm);
  694.  
  695.     /* Should really figure out a good offset value and test for that
  696.      * because we could still generate bogus numbers */
  697.     if (coord >= SHRT_MAX) {
  698.     coord = SHRT_MAX - 1000;
  699.     } else if (coord <= SHRT_MIN) {
  700.     coord = SHRT_MIN + 1000;
  701.     }
  702.     return (coord);
  703. }
  704.  
  705. /*
  706.  * ----------------------------------------------------------------------
  707.  *
  708.  * Blt_TransformPt --
  709.  *
  710.  *    Maps the given graph x,y coordinate values to a window position.
  711.  *
  712.  * Results:
  713.  *    Returns a XPoint structure containing the window coordinates of
  714.  *    the given graph x,y coordinate.
  715.  *
  716.  * ----------------------------------------------------------------------
  717.  */
  718. XPoint
  719. Blt_TransformPt(graphPtr, x, y, axisFlags)
  720.     Graph *graphPtr;
  721.     double x, y;
  722.     unsigned int axisFlags;    /* Specifies which axes to use */
  723. {
  724.     XPoint winPos;
  725.     enum AxisTypes axisType;
  726.  
  727.     axisType = (axisFlags & X1_AXIS_MASK) ? X1_AXIS : X2_AXIS;
  728.     winPos.x = Blt_Transform(graphPtr->axisArr[axisType], x);
  729.     axisType = (axisFlags & Y1_AXIS_MASK) ? Y1_AXIS : Y2_AXIS;
  730.     winPos.y = Blt_Transform(graphPtr->axisArr[axisType], y);
  731.     if (graphPtr->inverted) {    /* Swap x and y coordinates */
  732.     int coord;
  733.  
  734.     coord = winPos.y;
  735.     winPos.y = winPos.x;
  736.     winPos.x = coord;
  737.     }
  738.     return (winPos);
  739. }
  740.  
  741. /*
  742.  * ----------------------------------------------------------------------
  743.  *
  744.  * Blt_TransformDist --
  745.  *
  746.  *    Map the given graph x-coordinate value to a window position.
  747.  *
  748.  * Results:
  749.  *    Returns the window coordinate position at the given graph
  750.  *    x-coordinate.
  751.  *
  752.  * Note:
  753.  *    Since line and polygon clipping is performed by the X server,
  754.  *    we must be careful about coordinates which are outside of the
  755.  *      range of a signed short int.
  756.  *
  757.  * ----------------------------------------------------------------------
  758.  */
  759. int
  760. Blt_TransformDist(axis, value)
  761.     GraphAxis *axis;
  762.     double value;
  763. {
  764.     Axis *axisPtr = (Axis *)axis;
  765.     double norm;
  766.     int zero, coord;
  767.  
  768.     norm = Scale(axisPtr, 0.0);
  769.     zero = HORIZONTAL_AXIS(axisPtr)
  770.     ? MAPX(axisPtr, norm) : MAPY(axisPtr, norm);
  771.     norm = Scale(axisPtr, value);
  772.     coord = HORIZONTAL_AXIS(axisPtr)
  773.     ? MAPX(axisPtr, norm) : MAPY(axisPtr, norm);
  774.     coord -= zero;
  775.     return (BLT_ABS(coord));
  776. }
  777.  
  778. /*
  779.  * ----------------------------------------------------------------------
  780.  *
  781.  * Blt_PointOnGraph --
  782.  *
  783.  *    Determines if the window coordinates given represent a point
  784.  *    on the graph (within the bounds of the graph axes).
  785.  *
  786.  * Results:
  787.  *    Returns 1 is the point is within the bounds of the graph,
  788.  *    0 otherwise.
  789.  *
  790.  * ----------------------------------------------------------------------
  791.  */
  792. int
  793. Blt_PointOnGraph(graphPtr, pointPtr)
  794.     Graph *graphPtr;
  795.     XPoint *pointPtr;
  796. {
  797.     double norm;
  798.     Axis *axisPtr;
  799.  
  800.     /*
  801.      * It doesn't make a difference which x-axis or y-axis we use
  802.      * (the point is referenced by window coordinates). We're just
  803.      * checking if the point's within the range of axis' normalized
  804.      * coordinates [0..1].
  805.      */
  806.     axisPtr = (Axis *)graphPtr->bottomAxis;
  807.     if (axisPtr->scale == 0.0) {
  808.     return 0;        /* Axis layout hasn't been calculated yet */
  809.     }
  810.     norm = (pointPtr->x - axisPtr->offset) / axisPtr->scale;
  811.     if ((norm < 0.0) || (norm > 1.0)) {
  812.     return 0;        /* x-coordinates are off the graph */
  813.     }
  814.     axisPtr = (Axis *)graphPtr->leftAxis;
  815.     norm = (axisPtr->offset - pointPtr->y) / axisPtr->scale;
  816.     return ((norm >= 0.0) && (norm <= 1.0));
  817. }
  818.  
  819. /*
  820.  * ----------------------------------------------------------------------
  821.  *
  822.  * UpdateLimits --
  823.  *
  824.  *    Updates the min and max values for each axis as determined by
  825.  *    the data elements currently to be displayed.
  826.  *
  827.  * Results:
  828.  *    None.
  829.  *
  830.  * Side Effects:
  831.  *    Minimum, maximum data limit fields for both the X and Y axes
  832.  *    in the graph widget record are updated.
  833.  *
  834.  * ----------------------------------------------------------------------
  835.  */
  836. static void
  837. UpdateLimits(graphPtr, axisPtr)
  838.     Graph *graphPtr;
  839.     Axis *axisPtr;
  840. {
  841.     int result;
  842.  
  843.     if ((!AXIS_CONFIG_MAX_SET(axisPtr)) || (!AXIS_CONFIG_MIN_SET(axisPtr))) {
  844.     register Element *elemPtr;
  845.     Blt_ListEntry *entryPtr;
  846.     register double min, max;
  847.     double elemMin, elemMax;
  848.     double value;
  849.  
  850.     /* Find the minimum and maximum values for all the elements
  851.        displayed */
  852.     min = Blt_posInfinity, max = Blt_negInfinity;
  853.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  854.         entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  855.         elemPtr = (Element *)Blt_GetListValue(entryPtr);
  856.         if ((*elemPtr->limitsProc) (graphPtr, elemPtr,
  857.             (GraphAxis *)axisPtr, &elemMin, &elemMax) > 0) {
  858.         if (min > elemMin) {
  859.             min = elemMin;
  860.         }
  861.         if (max < elemMax) {
  862.             max = elemMax;
  863.         }
  864.         }
  865.     }
  866.     /*
  867.      * When auto-scaling, the axis limits are the bounds of the
  868.      * element data.  If no data exists, set arbitrary limits (wrt
  869.      * to log/linear scale).
  870.      */
  871.     if (min == Blt_posInfinity) {
  872.         min = (axisPtr->logScale) ? 0.001 : -10.0;
  873.     }
  874.     if (max == Blt_negInfinity) {
  875.         max = 10.0;
  876.     }
  877.     /*
  878.      * Handle situations where only one limit is set.
  879.      */
  880.     value = min;
  881.     if (AXIS_CONFIG_MIN_SET(axisPtr)) {
  882.         min = value = axisPtr->limits[LMIN];
  883.     } else if (AXIS_CONFIG_MAX_SET(axisPtr)) {
  884.         max = value = axisPtr->limits[LMAX];
  885.     }
  886.     /* If there's no range of data (min >= max), manufacture one */
  887.     if (min >= max) {
  888.         if (value == 0.0) {
  889.         min = -0.1, max = 0.1;
  890.         } else {
  891.         double x;
  892.  
  893.         x = BLT_FABS(value) * 0.1;
  894.         min = value - x;
  895.         max = value + x;
  896.         }
  897.     }
  898.     if (!AXIS_CONFIG_MIN_SET(axisPtr)) {
  899.         axisPtr->limits[LMIN] = min;
  900.     }
  901.     if (!AXIS_CONFIG_MAX_SET(axisPtr)) {
  902.         axisPtr->limits[LMAX] = max;
  903.     }
  904.     }
  905.     /* Indicate if the axis limits have changed */
  906.     result = (axisPtr->limits[LMAX] != axisPtr->prevMax) ||
  907.     (axisPtr->limits[LMIN] != axisPtr->prevMin);
  908.     /* and save the previous minimum and maximum values */
  909.     axisPtr->prevMin = axisPtr->limits[LMIN];
  910.     axisPtr->prevMax = axisPtr->limits[LMAX];
  911.     if (result) {
  912.     axisPtr->flags |= AXIS_CONFIG_DIRTY;
  913.     }
  914. }
  915.  
  916. /*
  917.  * ----------------------------------------------------------------------
  918.  *
  919.  * NiceNum --
  920.  *
  921.  *     Taken from Paul Heckbert's "Nice Numbers for Graph Labels" in
  922.  *    Graphics Gems (pp 61-63).  Finds a "nice" number approximately
  923.  *    equal to x.  Round the number if round=1, take ceiling if round=0.
  924.  *
  925.  * ----------------------------------------------------------------------
  926.  */
  927. static double
  928. NiceNum(x, round)
  929.     double x;
  930.     int round;
  931. {
  932.     double exponX;        /* exponent of x */
  933.     double fractX;        /* fractional part of x */
  934.     double nf;            /* nice, rounded fraction */
  935.  
  936.     exponX = floor(log10(x));
  937.     fractX = x / BLT_EXP10(exponX);    /* between 1 and 10 */
  938.     if (round) {
  939.     if (fractX < 1.5) {
  940.         nf = 1.;
  941.     } else if (fractX < 3.0) {
  942.         nf = 2.;
  943.     } else if (fractX < 7.0) {
  944.         nf = 5.;
  945.     } else {
  946.         nf = 10.;
  947.     }
  948.     } else if (fractX <= 1.0) {
  949.     nf = 1.;
  950.     } else if (fractX <= 2.0) {
  951.     nf = 2.;
  952.     } else if (fractX <= 5.0) {
  953.     nf = 5.0;
  954.     } else {
  955.     nf = 10.0;
  956.     }
  957.     return (nf * BLT_EXP10(exponX));
  958. }
  959.  
  960. /*
  961.  * ----------------------------------------------------------------------
  962.  *
  963.  * LogAxis --
  964.  *
  965.  *     Determine the range and units of a log scaled axis.
  966.  *
  967.  *     Unless the axis limits are specified, the axis is scaled
  968.  *     automatically, where the smallest and largest major ticks
  969.  *     encompass the range of actual data values.  When an axis
  970.  *     limit is specified, that value represents the
  971.  *     smallest(min)/largest(max) value in the displayed range of
  972.  *     values.
  973.  *
  974.  *     Both manual and automatic scaling are affected by the
  975.  *     step used.  By default, the step is the largest
  976.  *     power of ten to divide the range in more than one piece.
  977.  *
  978.  *     Automatic scaling:
  979.  *       Find the smallest number of units which contain the range of
  980.  *       values.  The minimum and maximum major tick values will be
  981.  *       represent the range of values for the axis. This greatest
  982.  *       number of major ticks possible is 10.
  983.  *
  984.  *     Manual scaling:
  985.  *       Make the minimum and maximum data values the represent the
  986.  *       range of the values for the axis.  The minimum and maximum
  987.  *       major ticks will be inclusive of this range.  This provides
  988.  *       the largest area for plotting and the expected results when
  989.  *       the axis min and max values have be set by the user (.e.g zooming).
  990.  *       The maximum number of major ticks is 20.
  991.  *
  992.  *       For log scale, there is always the possibility that the minimum
  993.  *       and maximum data values are the same magnitude.  To represent
  994.  *       the points properly, at least one full decade should be shown.
  995.  *       However, if you zoom a log scale plot, the results should be
  996.  *       predictable. Therefore, in that case, show only minor ticks.
  997.  *       Lastly, there should be an appropriate way to handle numbers <=0.
  998.  *
  999.  *          maxY
  1000.  *            |    units = magnitude (of least significant digit)
  1001.  *            |    high  = largest unit tick < max axis value
  1002.  *      high _|    low   = smallest unit tick > min axis value
  1003.  *            |
  1004.  *            |    range = high - low
  1005.  *            |    # ticks = greatest factor of range/units
  1006.  *           _|
  1007.  *        U   |
  1008.  *        n   |
  1009.  *        i   |
  1010.  *        t  _|
  1011.  *            |
  1012.  *            |
  1013.  *            |
  1014.  *       low _|
  1015.  *            |
  1016.  *            |_minX________________maxX__
  1017.  *            |   |       |      |       |
  1018.  *     minY  low                        high
  1019.  *           minY
  1020.  *
  1021.  *
  1022.  *     numTicks = Number of ticks
  1023.  *     min = Minimum value of axis
  1024.  *     max = Maximum value of axis
  1025.  *     range    = Range of values (max - min)
  1026.  *
  1027.  *     If the number of decades is greater than ten, it is assumed
  1028.  *    that the full set of log-style ticks can't be drawn properly.
  1029.  *
  1030.  * Results:
  1031.  *    None
  1032.  *
  1033.  * ----------------------------------------------------------------------
  1034.  */
  1035. static void
  1036. LogAxis(axisPtr)
  1037.     Axis *axisPtr;
  1038. {
  1039.     double range;
  1040.     double min, max;
  1041.  
  1042.     min = axisPtr->limits[LMIN];
  1043.     max = axisPtr->limits[LMAX];
  1044.  
  1045.     if (min > 0.0) {
  1046.     min = floor(log10(min));
  1047.     } else {
  1048.     min = 0.0;
  1049.     }
  1050.     if (max > 0.0) {
  1051.     max = ceil(log10(max));
  1052.     } else {
  1053.     max = 1.0;
  1054.     }
  1055.     range = max - min;
  1056.     if (range > 10) {
  1057.     range = NiceNum(range, 0);
  1058.     axisPtr->step = NiceNum(range / (NTICK - 1), 1);
  1059.  
  1060.     /* Find the outer limits in terms of the step. */
  1061.     min = UFLOOR(min, axisPtr->step);
  1062.     max = UCEIL(max, axisPtr->step);
  1063.     axisPtr->numTicks = (int)((max - min) / axisPtr->step) + 1;
  1064.     axisPtr->subStep = BLT_EXP10(floor(log10(axisPtr->step)));
  1065.  
  1066.     if (axisPtr->step == axisPtr->subStep) {
  1067.         axisPtr->subTicks = 5;
  1068.         axisPtr->subStep = axisPtr->step * 0.2;
  1069.     } else {
  1070.         axisPtr->subTicks = BLT_RND(axisPtr->step / axisPtr->subStep);
  1071.     }
  1072.     } else {
  1073.     if (min == max) {
  1074.         max++;
  1075.     }
  1076.     axisPtr->numTicks = (int)((max - min) + 1);
  1077.     axisPtr->step = 1.0;
  1078.     axisPtr->subTicks = 10;
  1079.     }
  1080.     axisPtr->min = axisPtr->tickMin = min;
  1081.     axisPtr->max = axisPtr->tickMax = max;
  1082.     axisPtr->range = (max - min);
  1083. #ifdef notdef
  1084.     fprintf(stderr, "Major: %s\nRegion min=%g,max=%g\nTick min=%g,max=%g\n\
  1085. numTicks=%d, range=%g, step=%.15g\n", axisNames[axisPtr->type], min, max,
  1086.     axisPtr->tickMin, axisPtr->tickMax, axisPtr->numTicks,
  1087.     axisPtr->range, axisPtr->step);
  1088.     fprintf(stderr, "Minor numTicks=%d, step=%.15g\n\n",
  1089.     axisPtr->subTicks, axisPtr->subStep);
  1090. #endif
  1091. }
  1092.  
  1093. /*
  1094.  * ----------------------------------------------------------------------
  1095.  *
  1096.  * LinearAxis --
  1097.  *
  1098.  *     Determine the units of a linear scaled axis.
  1099.  *
  1100.  *     Unless the axis limits are specified, the axis is scaled
  1101.  *     automatically, where the smallest and largest major ticks
  1102.  *     encompass the range of actual data values.  When an axis
  1103.  *     limit is specified, that value represents the
  1104.  *     smallest(min)/largest(max) value in the displayed range of
  1105.  *     values.
  1106.  *
  1107.  *     Both manual and automatic scaling are affected by the
  1108.  *     step used.  By default, the step is the largest
  1109.  *     power of ten to divide the range in more than one piece.
  1110.  *
  1111.  *     Automatic scaling:
  1112.  *       Find the smallest number of units which contain the range of
  1113.  *       values.  The minimum and maximum major tick values will be
  1114.  *       represent the range of values for the axis. This greatest
  1115.  *       number of major ticks possible is 10.
  1116.  *
  1117.  *     Manual scaling:
  1118.  *       Make the minimum and maximum data values the represent the
  1119.  *       range of the values for the axis.  The minimum and maximum
  1120.  *       major ticks will be inclusive of this range.  This provides
  1121.  *       the largest area for plotting and the expected results when
  1122.  *       the axis min and max values have be set by the user (.e.g zooming).
  1123.  *       The maximum number of major ticks is 20.
  1124.  *
  1125.  *       For log scale, there is always the possibility that the minimum
  1126.  *       and maximum data values are the same magnitude.  To represent
  1127.  *       the points properly, at least one full decade should be shown.
  1128.  *       However, if you zoom a log scale plot, the results should be
  1129.  *       predictable. Therefore, in that case, show only minor ticks.
  1130.  *       Lastly, there should be an appropriate way to handle numbers <=0.
  1131.  *
  1132.  *          maxY
  1133.  *            |    units = magnitude (of least significant digit)
  1134.  *            |    high  = largest unit tick < max axis value
  1135.  *      high _|    low   = smallest unit tick > min axis value
  1136.  *            |
  1137.  *            |    range = high - low
  1138.  *            |    # ticks = greatest factor of range/units
  1139.  *           _|
  1140.  *        U   |
  1141.  *        n   |
  1142.  *        i   |
  1143.  *        t  _|
  1144.  *            |
  1145.  *            |
  1146.  *            |
  1147.  *       low _|
  1148.  *            |
  1149.  *            |_minX________________maxX__
  1150.  *            |   |       |      |       |
  1151.  *     minY  low                        high
  1152.  *           minY
  1153.  *
  1154.  *
  1155.  *     numTicks = Number of ticks
  1156.  *     min = Minimum value of axis
  1157.  *     max = Maximum value of axis
  1158.  *     range    = Range of values (max - min)
  1159.  *
  1160.  * Results:
  1161.  *    None.
  1162.  *
  1163.  * ----------------------------------------------------------------------
  1164.  */
  1165. static void
  1166. LinearAxis(axisPtr)
  1167.     Axis *axisPtr;
  1168. {
  1169.     double range, unit, pad;
  1170.     double min, max;
  1171.  
  1172.     min = axisPtr->limits[LMIN];
  1173.     max = axisPtr->limits[LMAX];
  1174.  
  1175.     /*
  1176.      * Calculate the major step.
  1177.      */
  1178.     range = max - min;
  1179.  
  1180.     if ((axisPtr->reqStep > 0.0) && (axisPtr->reqStep < range)) {
  1181.     axisPtr->step = axisPtr->reqStep;
  1182.     } else {
  1183.     range = NiceNum(range, 0);
  1184.     axisPtr->step = NiceNum(range / (NTICK - 1), 1);
  1185.     }
  1186.  
  1187.     /*
  1188.      * Find the outer tick values in terms of the major step interval.
  1189.      * Add +0.0 to preclude the possibility of an IEEE -0.0.
  1190.      */
  1191.  
  1192.     axisPtr->tickMin = UFLOOR(min, axisPtr->step) + 0.0;
  1193.     axisPtr->tickMax = UCEIL(max, axisPtr->step) + 0.0;
  1194.     range = axisPtr->tickMax - axisPtr->tickMin;
  1195.     unit = range / axisPtr->step;
  1196.     axisPtr->numTicks = BLT_RND(unit) + 1;
  1197.  
  1198.     /*
  1199.      * If the axis is "loose", the range is between the two outermost
  1200.      * ticks. Otherwise if it's "tight", the range is between the data
  1201.      * min and max.
  1202.      */
  1203.     if (axisPtr->loose) {
  1204.     axisPtr->min = axisPtr->tickMin, axisPtr->max = axisPtr->tickMax;
  1205.     } else {
  1206.     axisPtr->min = min, axisPtr->max = max;
  1207.     }
  1208.  
  1209.     /*
  1210.      * If is a limit is auto-scaled, add some padding so that the
  1211.      * symbols representing data points at the extremes aren't clipped
  1212.      * in half by the edge of the plot.  Two percent is an arbitrary
  1213.      * guess.
  1214.      */
  1215.  
  1216.     pad = (axisPtr->max - axisPtr->min) * 0.02;
  1217.     if (!AXIS_CONFIG_MIN_SET(axisPtr)) {
  1218.     axisPtr->min -= pad;
  1219.     }
  1220.     if (!AXIS_CONFIG_MAX_SET(axisPtr)) {
  1221.     axisPtr->max += pad;
  1222.     }
  1223.     axisPtr->range = axisPtr->max - axisPtr->min;
  1224.  
  1225. #ifdef notdef
  1226.     fprintf(stderr, "Major: %s\nRegion min=%g,max=%g\nTick min=%g,max=%g\n\
  1227. numTicks=%d, range=%g, step=%.15g\n", axisNames[axisPtr->type], min, max,
  1228.     axisPtr->tickMin, axisPtr->tickMax, axisPtr->numTicks,
  1229.     axisPtr->range, axisPtr->step);
  1230. #endif
  1231.  
  1232.     /* Now calculate the minor tick step and number. */
  1233.     axisPtr->subTicks = axisPtr->reqSubTicks;
  1234.     if (axisPtr->subTicks < 0) {
  1235.     axisPtr->subTicks = 0;
  1236.     }
  1237.     if (axisPtr->subTicks > 0) {
  1238.     axisPtr->subStep = axisPtr->step / axisPtr->subTicks;
  1239.     } else {
  1240.     axisPtr->subStep = axisPtr->step * 0.2;    /* Need this for layout */
  1241.     }
  1242. #ifdef notdef
  1243.     fprintf(stderr, "Minor numTicks=%d, step=%.15g\n\n",
  1244.     axisPtr->subTicks, axisPtr->subStep);
  1245. #endif
  1246. }
  1247.  
  1248. /*
  1249.  * -----------------------------------------------------------------
  1250.  *
  1251.  * SetAxisLimits  --
  1252.  *
  1253.  * -----------------------------------------------------------------
  1254.  */
  1255. static void
  1256. SetAxisLimits(graphPtr, axisPtr)
  1257.     Graph *graphPtr;
  1258.     Axis *axisPtr;
  1259. {
  1260.     UpdateLimits(graphPtr, axisPtr);
  1261.     /*
  1262.      * For barcharts, adjust the min or max values to include 0.0
  1263.      * if the bars are drawn along this axis.
  1264.      */
  1265.     if ((graphPtr->type == BARCHART) && (Y_AXIS(axisPtr))) {
  1266.     if (!AXIS_CONFIG_MIN_SET(axisPtr) && (axisPtr->limits[LMIN] > 0.0)) {
  1267.         axisPtr->limits[LMIN] = 0.0;
  1268.     }
  1269.     if (!AXIS_CONFIG_MAX_SET(axisPtr) && (axisPtr->limits[LMAX] < 0.0)) {
  1270.         axisPtr->limits[LMAX] = 0.0;
  1271.     }
  1272.     }
  1273.     if (axisPtr->flags & AXIS_CONFIG_DIRTY) {
  1274.     /* Calculate min/max tick (major/minor) layouts */
  1275.     if (axisPtr->logScale) {
  1276.         LogAxis(axisPtr);
  1277.     } else {
  1278.         LinearAxis(axisPtr);
  1279.     }
  1280.     axisPtr->flags &= ~AXIS_CONFIG_DIRTY;
  1281.  
  1282.     /* When any axis changes, we need to layout the entire graph. */
  1283.     graphPtr->flags |= (LAYOUT_ALL | DIRTY | REFRESH);
  1284.     }
  1285. }
  1286.  
  1287. /*
  1288.  * ----------------------------------------------------------------------
  1289.  *
  1290.  * Blt_ComputeAxes --
  1291.  *
  1292.  * Results:
  1293.  *    None.
  1294.  *
  1295.  * ----------------------------------------------------------------------
  1296.  */
  1297. void
  1298. Blt_ComputeAxes(graphPtr)
  1299.     Graph *graphPtr;
  1300. {
  1301.     register int i;
  1302.  
  1303.     for (i = 0; i < 4; i++) {
  1304.     SetAxisLimits(graphPtr, (Axis *)graphPtr->axisArr[i]);
  1305.     }
  1306. }
  1307.  
  1308. /*
  1309.  * ----------------------------------------------------------------------
  1310.  *
  1311.  * ConfigureAxis --
  1312.  *
  1313.  *    Configures axis attributes (font, line width, label, etc) and
  1314.  *    allocates a new (possibly shared) graphics context.  Line cap
  1315.  *    style is projecting.  This is for the problem of when a tick
  1316.  *    sits directly at the end point of the axis.
  1317.  *
  1318.  * Results:
  1319.  *    The return value is a standard Tcl result.
  1320.  *
  1321.  * Side Effects:
  1322.  *    Axis resources are allocated (GC, font). Axis layout is
  1323.  *    deferred until the height and width of the window are known.
  1324.  *
  1325.  * ----------------------------------------------------------------------
  1326.  */
  1327. static int
  1328. ConfigureAxis(graphPtr, axisPtr, argc, argv, flags)
  1329.     Graph *graphPtr;
  1330.     Axis *axisPtr;
  1331.     int argc;
  1332.     char *argv[];
  1333.     int flags;
  1334. {
  1335.     GC newGC;
  1336.     XGCValues gcValues;
  1337.     unsigned long gcMask;
  1338.     Tk_ConfigSpec *configSpecs;
  1339.  
  1340.     configSpecs = axisConfigSpecs[axisPtr->type];
  1341.     if (flags & TK_CONFIG_ARGV_ONLY) {
  1342.     if (argc == 0) {
  1343.         return (Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin,
  1344.             configSpecs, (char *)axisPtr, (char *)NULL, flags));
  1345.     } else if (argc == 1) {
  1346.         return (Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin,
  1347.             configSpecs, (char *)axisPtr, argv[0], flags));
  1348.     }
  1349.     }
  1350.     if (Tk_ConfigureWidget(graphPtr->interp, graphPtr->tkwin, configSpecs,
  1351.         argc, argv, (char *)axisPtr, flags) != TCL_OK) {
  1352.     return TCL_ERROR;
  1353.     }
  1354.     /*
  1355.      * Check requested X and Y axis limits. Can't allow min to be
  1356.      * greater than max, or have undefined log scale limits.
  1357.      */
  1358.     if ((AXIS_CONFIG_MIN_SET(axisPtr)) && (AXIS_CONFIG_MAX_SET(axisPtr)) &&
  1359.     (axisPtr->limits[LMIN] >= axisPtr->limits[LMAX])) {
  1360.     sprintf(graphPtr->interp->result,
  1361.         "impossible %s-axis limits (min %g >= max %g)",
  1362.         axisNames[axisPtr->type], axisPtr->limits[LMIN],
  1363.         axisPtr->limits[LMAX]);
  1364.     return TCL_ERROR;
  1365.     }
  1366.     if ((axisPtr->logScale) && (AXIS_CONFIG_MIN_SET(axisPtr)) &&
  1367.     (axisPtr->limits[LMIN] <= 0.0)) {
  1368.     sprintf(graphPtr->interp->result,
  1369.         "invalid %s-axis limits (min=%g,max=%g) for log scale",
  1370.         axisNames[axisPtr->type], axisPtr->limits[LMIN],
  1371.         axisPtr->limits[LMAX]);
  1372.     return TCL_ERROR;
  1373.     }
  1374.     /*
  1375.      * Reset bogus line widths to zero. Can't allow bad line widths
  1376.      * because the layout routines compute axis and tick positions
  1377.      * with them.
  1378.      */
  1379.     if (axisPtr->lineWidth < 1) {
  1380.     axisPtr->lineWidth = 0;
  1381.     }
  1382.     /*
  1383.      * Create an unshared GC for the tick labels. The GC is private
  1384.      * because the labels may be rotated, requiring the GCStipple and
  1385.      * GCTSOffset fields to change.
  1386.      */
  1387.     gcMask = GCForeground | GCFont;
  1388.     gcValues.font = axisPtr->fontPtr->fid;
  1389.     gcValues.foreground = axisPtr->fgColorPtr->pixel;
  1390.     if (graphPtr->border != NULL) {
  1391.     gcValues.background = Tk_3DBorderColor(graphPtr->border)->pixel;
  1392.     gcMask |= GCBackground;
  1393.     }
  1394.     newGC = XCreateGC(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
  1395.     gcMask, &gcValues);
  1396.     if (axisPtr->textGC != NULL) {
  1397.     XFreeGC(graphPtr->display, axisPtr->textGC);
  1398.     }
  1399.     axisPtr->textGC = newGC;
  1400.  
  1401.     /* Create GC for axis line and ticks. */
  1402.  
  1403.     gcMask = GCForeground | GCLineWidth | GCCapStyle;
  1404.     gcValues.line_width = axisPtr->lineWidth;
  1405.     gcValues.cap_style = CapProjecting;
  1406.     newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
  1407.     if (axisPtr->lineGC != NULL) {
  1408.     Tk_FreeGC(graphPtr->display, axisPtr->lineGC);
  1409.     }
  1410.     axisPtr->lineGC = newGC;
  1411.  
  1412.     /*
  1413.      * Don't bother to check what options have changed.  Almost every
  1414.      * axis configuration option changes the size of the plotting area
  1415.      * (except for -foreground).
  1416.      */
  1417.     graphPtr->flags |= LAYOUT_ALL;
  1418.     axisPtr->flags |= AXIS_CONFIG_DIRTY;
  1419.     SetAxisLimits(graphPtr, axisPtr);
  1420.     Blt_RedrawGraph(graphPtr);
  1421.     return TCL_OK;
  1422. }
  1423.  
  1424. /*
  1425.  * ----------------------------------------------------------------------
  1426.  *
  1427.  * DestroyAxis --
  1428.  *
  1429.  * Results:
  1430.  *    None.
  1431.  *
  1432.  * Side effects:
  1433.  *    Resources (font, color, gc, labels, etc.) associated with the
  1434.  *    axis are deallocated.
  1435.  *
  1436.  * ----------------------------------------------------------------------
  1437.  */
  1438. static void
  1439. DestroyAxis(graphPtr, axis)
  1440.     Graph *graphPtr;
  1441.     GraphAxis *axis;
  1442. {
  1443.     Axis *axisPtr = (Axis *)axis;
  1444.  
  1445.     Tk_FreeOptions(axisConfigSpecs[axisPtr->type], (char *)axisPtr,
  1446.     graphPtr->display, 0);
  1447.  
  1448.     if (axisPtr->lineGC != NULL) {
  1449.     Tk_FreeGC(graphPtr->display, axisPtr->lineGC);
  1450.     }
  1451.     if (axisPtr->textGC != NULL) {
  1452.     XFreeGC(graphPtr->display, axisPtr->textGC);
  1453.     }
  1454.     if (axisPtr->labelArr != NULL) {
  1455.     free((char *)axisPtr->labelArr);
  1456.     }
  1457.     if (axisPtr->segArr != NULL) {
  1458.     free((char *)axisPtr->segArr);
  1459.     }
  1460.     free((char *)axisPtr);
  1461. }
  1462.  
  1463. /*
  1464.  * ----------------------------------------------------------------------
  1465.  *
  1466.  * CalculateOffsets --
  1467.  *
  1468.  *    Determines the placements of the axis, major and minor ticks,
  1469.  *    and title of the axis.
  1470.  *
  1471.  * Results:
  1472.  *    None.
  1473.  *
  1474.  * ----------------------------------------------------------------------
  1475.  */
  1476. static void
  1477. CalculateOffsets(graphPtr, axisPtr)
  1478.     Graph *graphPtr;
  1479.     Axis *axisPtr;
  1480. {
  1481.     int pad;            /* Offset of axis from interior region. This
  1482.                  * includes a possible border and the axis
  1483.                  * line width. */
  1484.     unsigned int textHeight;
  1485.     int innerPos, outerPos;
  1486.     int majorOffset, minorOffset, labelOffset, titleOffset;
  1487.  
  1488.     textHeight = TEXTHEIGHT(graphPtr->fontPtr);
  1489.  
  1490.     titleOffset = graphPtr->borderWidth + textHeight;
  1491.     majorOffset = BLT_ABS(axisPtr->tickLength);
  1492.     minorOffset = BLT_RND(majorOffset * 0.5);
  1493.     labelOffset = BLT_RND(majorOffset * 1.4) + axisPtr->lineWidth / 2;
  1494.  
  1495.     /* Adjust offset for the interior border width and the line width */
  1496.     pad = graphPtr->plotBW + axisPtr->lineWidth + 2;
  1497.     if (graphPtr->plotBW > 0) {
  1498.     pad++;
  1499.     }
  1500.     if ((axisPtr->location == LEFT_AXIS) || (axisPtr->location == TOP_AXIS)) {
  1501.     majorOffset = -majorOffset;
  1502.     minorOffset = -minorOffset;
  1503.     labelOffset = -labelOffset;
  1504.     }
  1505.     /*
  1506.      * Pre-calculate the x-coordinate positions of the axis,
  1507.      * tick labels, and the individual major and minor ticks.
  1508.      */
  1509.     switch (axisPtr->location) {
  1510.     case BOTTOM_AXIS:
  1511.     innerPos = graphPtr->origin.y + pad;
  1512.     axisPtr->titlePos.x = (graphPtr->extreme.x + graphPtr->origin.x) / 2;
  1513.     axisPtr->titlePos.y = graphPtr->height - titleOffset;
  1514.     axisPtr->anchor = TK_ANCHOR_N;
  1515.     break;
  1516.  
  1517.     case LEFT_AXIS:
  1518.     innerPos = graphPtr->origin.x - pad;
  1519.     axisPtr->titlePos.x = titleOffset;
  1520.     axisPtr->titlePos.y = (graphPtr->origin.y + graphPtr->extreme.y) / 2;
  1521.     axisPtr->anchor = TK_ANCHOR_E;
  1522.     break;
  1523.  
  1524.     case TOP_AXIS:
  1525.     innerPos = graphPtr->extreme.y - pad;
  1526.     axisPtr->titlePos.x = (graphPtr->extreme.x + graphPtr->origin.x) / 2;
  1527.     axisPtr->titlePos.y = titleOffset;
  1528.     if (graphPtr->title != NULL) {
  1529.         axisPtr->titlePos.y += (2 * textHeight);
  1530.     } else {
  1531.         axisPtr->titlePos.y += (textHeight / 2);
  1532.     }
  1533.     axisPtr->anchor = TK_ANCHOR_S;
  1534.     break;
  1535.  
  1536.     case RIGHT_AXIS:
  1537.     innerPos = graphPtr->extreme.x + pad;
  1538.     axisPtr->titlePos.x = graphPtr->width -
  1539.         (graphPtr->legendPtr->width + titleOffset);
  1540.     axisPtr->titlePos.y = (graphPtr->origin.y + graphPtr->extreme.y) / 2;
  1541.     axisPtr->anchor = TK_ANCHOR_W;
  1542.     break;
  1543.  
  1544.     default:
  1545.     abort();
  1546.     }
  1547.     outerPos = innerPos + majorOffset;
  1548.     axisPtr->posArr[MAJOR_TICK] = outerPos;
  1549.     axisPtr->posArr[AXIS_LINE] = innerPos;
  1550.     axisPtr->posArr[MINOR_TICK] = innerPos + minorOffset;
  1551.     axisPtr->posArr[TICK_LABEL] = innerPos + labelOffset;
  1552.     if (axisPtr->tickLength < 0) {
  1553.     axisPtr->posArr[MAJOR_TICK] = innerPos;
  1554.     axisPtr->posArr[AXIS_LINE] = outerPos;
  1555.     }
  1556. }
  1557.  
  1558. static XSegment
  1559. AxisLine(axisPtr, min, max)
  1560.     Axis *axisPtr;        /* Axis information */
  1561.     double min, max;        /* Limits of axis in graph coordinates */
  1562. {
  1563.     double normMin, normMax;
  1564.     XSegment segment;
  1565.  
  1566.     normMax = NORM(axisPtr, min);
  1567.     if (axisPtr->descending) {
  1568.     normMax = 1.0 - normMax;
  1569.     }
  1570.     normMin = NORM(axisPtr, max);
  1571.     if (axisPtr->descending) {
  1572.     normMin = 1.0 - normMin;
  1573.     }
  1574.     if (HORIZONTAL_AXIS(axisPtr)) {
  1575.     segment.y1 = segment.y2 = axisPtr->posArr[AXIS_LINE];
  1576.     segment.x1 = MAPX(axisPtr, normMin);
  1577.     segment.x2 = MAPX(axisPtr, normMax);
  1578.     } else {
  1579.     segment.x1 = segment.x2 = axisPtr->posArr[AXIS_LINE];
  1580.     segment.y1 = MAPY(axisPtr, normMin);
  1581.     segment.y2 = MAPY(axisPtr, normMax);
  1582.     }
  1583.     return (segment);
  1584. }
  1585.  
  1586.  
  1587. static XSegment
  1588. Tick(axisPtr, value, whichTick)
  1589.     Axis *axisPtr;
  1590.     double value;
  1591.     int whichTick;        /* If non-zero, create minor tick instead */
  1592. {
  1593.     double norm;
  1594.     XSegment segment;
  1595.     int tick;
  1596.  
  1597.     norm = NORM(axisPtr, value);
  1598.     if (axisPtr->descending) {
  1599.     norm = 1.0 - norm;
  1600.     }
  1601.     tick = axisPtr->posArr[whichTick];
  1602.     if (HORIZONTAL_AXIS(axisPtr)) {
  1603.     segment.y1 = axisPtr->posArr[AXIS_LINE];
  1604.     segment.y2 = tick;
  1605.     segment.x1 = segment.x2 = MAPX(axisPtr, norm);
  1606.     } else {
  1607.     segment.x1 = axisPtr->posArr[AXIS_LINE];
  1608.     segment.x2 = tick;
  1609.     segment.y1 = segment.y2 = MAPY(axisPtr, norm);
  1610.     }
  1611.     return (segment);
  1612. }
  1613.  
  1614. /*
  1615.  * -----------------------------------------------------------------
  1616.  *
  1617.  * LayoutAxis --
  1618.  *
  1619.  *    Pre-calculate the x-coordinate positions of the axis, ticks
  1620.  *    and labels to be used later when displaying the X axis.  Ticks
  1621.  *    (minor and major) will be saved in an array of XSegments so
  1622.  *    that they can be drawn in one XDrawSegments call. The strings
  1623.  *    representing the tick labels and the corresponding window
  1624.  *    positions are saved in an array of Label's.
  1625.  *
  1626.  *      Calculates the values for each major and minor tick and checks to
  1627.  *    see if they are in range (the outer ticks may be outside of the
  1628.  *    range of plotted values).
  1629.  *
  1630.  * Results:
  1631.  *    None.
  1632.  *
  1633.  * SideEffects:
  1634.  *    Line segments and tick labels saved will be used to draw
  1635.  *    the X axis.
  1636.  * ----------------------------------------------------------------- */
  1637. static void
  1638. LayoutAxis(graphPtr, axis)
  1639.     Graph *graphPtr;
  1640.     GraphAxis *axis;
  1641. {
  1642.     Axis *axisPtr = (Axis *)axis;
  1643.     XSegment *segArr;
  1644.     unsigned int arraySize;
  1645.     double min, max;
  1646.     double value, subValue;
  1647.     register int i, j;
  1648.     register int sgmts, labels;
  1649.     double epsilon;
  1650.     static float logTable[] =    /* Precomputed log10 values [1..10] */
  1651.     {
  1652.     0.0, 0.301, 0.477, 0.602, 0.699, 0.778, 0.845, 0.903, 0.954, 1.0
  1653.     };
  1654.  
  1655.     CalculateOffsets(graphPtr, axisPtr);
  1656.  
  1657.     /* Save all line coordinates in an array of line segments. */
  1658.  
  1659.     arraySize = (1 + (axisPtr->numTicks * (axisPtr->subTicks + 1)));
  1660.     segArr = (XSegment *)malloc(arraySize * sizeof(XSegment));
  1661.     if (segArr == NULL) {
  1662.     return;            /* Can't allocate array of segments */
  1663.     }
  1664.     if ((axisPtr->logScale) || (axisPtr->loose) ||
  1665.     (axisPtr->limits[LMIN] == axisPtr->limits[LMAX])) {
  1666.     min = axisPtr->tickMin, max = axisPtr->tickMax;
  1667.     } else {
  1668.     min = axisPtr->limits[LMIN];
  1669.     max = axisPtr->limits[LMAX];
  1670.     }
  1671.  
  1672.     /* Axis baseline */
  1673.     segArr[0] = AxisLine(axisPtr, min, max);
  1674.  
  1675.     sgmts = 1, labels = 0;
  1676.     if (!axisPtr->showTicks) {
  1677.     goto done;        /* Only display axis line */
  1678.     }
  1679.     /* Use numbers just beyond the limits when testing for equality */
  1680.     epsilon = 2 * MIN_DBL_VALUE;
  1681.     min -= epsilon, max += epsilon;
  1682.  
  1683.     value = axisPtr->tickMin;    /* Start from smallest axis tick */
  1684.     for (i = 0; i < axisPtr->numTicks; i++) {
  1685.     subValue = value = UROUND(value, axisPtr->step);
  1686.  
  1687.     /* Minor ticks */
  1688.     for (j = 1; j < axisPtr->subTicks; j++) {
  1689.         if ((axisPtr->logScale) && (axisPtr->step == 1.0)) {
  1690.         subValue = value + (double)logTable[j];
  1691.         } else {
  1692.         subValue += axisPtr->subStep;
  1693.         }
  1694.         if ((subValue >= min) && (subValue <= max)) {
  1695.         segArr[sgmts] = Tick(axisPtr, subValue, MINOR_TICK);
  1696.         sgmts++;
  1697.         }
  1698.     }
  1699.  
  1700.     /* Major tick and label */
  1701.     if ((value >= min) && (value <= max)) {
  1702.         short int labelPos;
  1703.  
  1704.         segArr[sgmts] = Tick(axisPtr, value, MAJOR_TICK);
  1705.         labelPos = (short int)axisPtr->posArr[TICK_LABEL];
  1706.  
  1707.         /* Save tick label position */
  1708.  
  1709.         if (HORIZONTAL_AXIS(axisPtr)) {
  1710.         axisPtr->labelArr[labels].x = segArr[sgmts].x1;
  1711.         axisPtr->labelArr[labels].y = labelPos;
  1712.         } else {
  1713.         axisPtr->labelArr[labels].x = labelPos;
  1714.         axisPtr->labelArr[labels].y = segArr[sgmts].y1;
  1715.         }
  1716.         sgmts++, labels++;
  1717.     }
  1718.     value += axisPtr->step;
  1719.     }
  1720.  
  1721.   done:
  1722.  
  1723.     assert(sgmts <= arraySize);
  1724.     assert(labels <= axisPtr->numLabels);
  1725.  
  1726.     if (axisPtr->segArr != NULL) {
  1727.     free((char *)axisPtr->segArr);
  1728.     }
  1729.     axisPtr->segArr = segArr;
  1730.     axisPtr->numSegments = sgmts;
  1731. }
  1732.  
  1733. /*
  1734.  * -----------------------------------------------------------------
  1735.  *
  1736.  * DisplayAxis --
  1737.  *
  1738.  *    Draws the axis, ticks, and labels onto the canvas.
  1739.  *
  1740.  *    Initializes and passes text attribute information through
  1741.  *    TextAttributes structure.
  1742.  *
  1743.  * Results:
  1744.  *    None.
  1745.  *
  1746.  * Side Effects:
  1747.  *    Axis gets drawn on window.
  1748.  *
  1749.  * -----------------------------------------------------------------
  1750.  */
  1751.  
  1752. static float titleRot[4] =    /* Rotation for each axis title */
  1753. {
  1754.     0.0, 90.0, 0.0, 270.0
  1755. };
  1756.  
  1757. static void
  1758. DisplayAxis(graphPtr, axis, attrPtr)
  1759.     Graph *graphPtr;
  1760.     GraphAxis *axis;
  1761.     TextAttributes *attrPtr;
  1762. {
  1763.     Axis *axisPtr = (Axis *)axis;
  1764.  
  1765.     if (axisPtr->title != NULL) {
  1766.     attrPtr->theta = (double)titleRot[axisPtr->location];
  1767.     Blt_DrawText(graphPtr->display, graphPtr->canvas, axisPtr->title,
  1768.         attrPtr, axisPtr->titlePos.x, axisPtr->titlePos.y);
  1769.     }
  1770.     if (axisPtr->showTicks) {
  1771.     register int i;
  1772.     TextAttributes textAttr;
  1773.  
  1774.     /* Setup static text attribute information */
  1775.  
  1776.     textAttr.theta = axisPtr->theta;
  1777.     textAttr.anchor = axisPtr->anchor;
  1778.     textAttr.fontPtr = axisPtr->fontPtr;
  1779.     textAttr.fgColorPtr = axisPtr->fgColorPtr;
  1780.     textAttr.bgColorPtr = Tk_3DBorderColor(graphPtr->border);
  1781.     textAttr.gc = axisPtr->textGC;
  1782.  
  1783.     /* Draw the ticks labels and then the ticks and axis */
  1784.     for (i = 0; i < axisPtr->numLabels; i++) {
  1785.         Blt_DrawText(graphPtr->display, graphPtr->canvas,
  1786.         axisPtr->labelArr[i].text, &textAttr,
  1787.         axisPtr->labelArr[i].x, axisPtr->labelArr[i].y);
  1788.     }
  1789.     }
  1790.     if (axisPtr->numSegments > 0) {
  1791.     XDrawSegments(graphPtr->display, graphPtr->canvas, axisPtr->lineGC,
  1792.         axisPtr->segArr, axisPtr->numSegments);
  1793.     }
  1794. }
  1795.  
  1796. /*
  1797.  * -----------------------------------------------------------------
  1798.  *
  1799.  * PrintAxis --
  1800.  *
  1801.  *    Generates PostScript output to draw the axis, ticks, and
  1802.  *    labels.
  1803.  *
  1804.  *    Initializes and passes text attribute information through
  1805.  *    TextAttributes structure.
  1806.  *
  1807.  * Results:
  1808.  *    None.
  1809.  *
  1810.  * Side Effects:
  1811.  *    PostScript output is left in graphPtr->interp->result;
  1812.  *
  1813.  * -----------------------------------------------------------------
  1814.  */
  1815. static void
  1816. PrintAxis(graphPtr, axis, attrPtr)
  1817.     Graph *graphPtr;
  1818.     GraphAxis *axis;
  1819.     TextAttributes *attrPtr;
  1820. {
  1821.     Axis *axisPtr = (Axis *)axis;
  1822.  
  1823.     if (axisPtr->title != NULL) {
  1824.     attrPtr->theta = (double)titleRot[axisPtr->location];
  1825.     Blt_TextToPostScript(graphPtr, axisPtr->title, attrPtr,
  1826.         axisPtr->titlePos.x, axisPtr->titlePos.y);
  1827.     }
  1828.     if (axisPtr->showTicks) {
  1829.     TextAttributes textAttr;
  1830.     register int i;
  1831.  
  1832.     /* Setup static text attribute information */
  1833.  
  1834.     textAttr.theta = axisPtr->theta;
  1835.     textAttr.anchor = axisPtr->anchor;
  1836.     textAttr.fontPtr = axisPtr->fontPtr;
  1837.     textAttr.fgColorPtr = axisPtr->fgColorPtr;
  1838.     textAttr.bgColorPtr = (XColor *)NULL;
  1839.  
  1840.     for (i = 0; i < axisPtr->numLabels; i++) {
  1841.         Blt_TextToPostScript(graphPtr, axisPtr->labelArr[i].text,
  1842.         &textAttr, axisPtr->labelArr[i].x, axisPtr->labelArr[i].y);
  1843.     }
  1844.     }
  1845.     if (axisPtr->numSegments > 0) {
  1846.     Blt_SetLineAttributes(graphPtr, axisPtr->fgColorPtr,
  1847.         axisPtr->lineWidth, 0);
  1848.     Blt_SegmentsToPostScript(graphPtr, axisPtr->segArr,
  1849.         axisPtr->numSegments);
  1850.     }
  1851. }
  1852.  
  1853. static void
  1854. GetAxisGeometry(graphPtr, axisPtr)
  1855.     Graph *graphPtr;
  1856.     Axis *axisPtr;
  1857. {
  1858.     register int i;
  1859.     register int count;
  1860.     char label[LABEL_MAX_SIZE+1];
  1861.     unsigned int length;
  1862.     unsigned int arraySize, poolSize, used;
  1863.     char *pool;
  1864.     unsigned int textWidth, textHeight;
  1865.     unsigned int bbWidth, bbHeight;
  1866.     int maxWidth, maxHeight;
  1867.     double value;
  1868.     double epsilon, minAxis, maxAxis;
  1869.     Label *labelArr;
  1870.     int pad;
  1871.  
  1872.     if ((axisPtr->logScale) || (axisPtr->loose) ||
  1873.     (axisPtr->limits[LMIN] == axisPtr->limits[LMAX])) {
  1874.     minAxis = axisPtr->tickMin, maxAxis = axisPtr->tickMax;
  1875.     } else {
  1876.     minAxis = axisPtr->limits[LMIN];
  1877.     maxAxis = axisPtr->limits[LMAX];
  1878.     }
  1879.  
  1880.     /* Use numbers just beyond the limits when testing for equality */
  1881.  
  1882.     epsilon = 2 * MIN_DBL_VALUE;
  1883.     minAxis -= epsilon, maxAxis += epsilon;
  1884.  
  1885.     /* Create an array of labels with an attached of characters strings */
  1886.     arraySize = axisPtr->numTicks * sizeof(Label);
  1887.     poolSize = axisPtr->numTicks * AVG_TICK_NUM_CHARS * sizeof(char);
  1888.     labelArr = (Label *)malloc(arraySize + poolSize);
  1889.     pool = (char *)labelArr + arraySize;
  1890.  
  1891.     textHeight = TEXTHEIGHT(axisPtr->fontPtr);
  1892.  
  1893.     maxHeight = maxWidth = 0;
  1894.     used = count = 0;
  1895.     value = axisPtr->tickMin;
  1896.     for (i = 0; i < axisPtr->numTicks; i++, value += axisPtr->step) {
  1897.     value = UROUND(value, axisPtr->step);
  1898.     if ((value < minAxis) || (value > maxAxis)) {
  1899.         continue;        /* Out of range */
  1900.     }
  1901.     MakeLabel(graphPtr, axisPtr, value, label);
  1902.     length = strlen(label);
  1903.  
  1904.     /*  Resize the label array if we overflow its string pool */
  1905.     if (poolSize <= (used + length + 1)) {
  1906.         int newSize;
  1907.         Label *newArr;
  1908.  
  1909.         newSize = poolSize + poolSize;
  1910.         while (newSize <= (used + length + 1)) {
  1911.         newSize += newSize;
  1912.         }
  1913.         newArr = (Label *)malloc(arraySize + newSize);
  1914.         memcpy((char *)newArr, (char *)labelArr, arraySize + poolSize);
  1915.         pool = (char *)newArr + arraySize;
  1916.         free((char *)labelArr);
  1917.         poolSize = newSize;
  1918.         labelArr = newArr;
  1919.     }
  1920.     textWidth = Blt_TextStringWidth(axisPtr->fontPtr, label);
  1921.     labelArr[count].text = (pool + used);
  1922.     strcpy(labelArr[count].text, label);
  1923.     used += (length + 1);
  1924.     count++;
  1925.  
  1926.     if (axisPtr->theta == 0.0) {
  1927.         bbWidth = textWidth, bbHeight = textHeight;
  1928.     } else {
  1929.         Blt_GetBoundingBox(textWidth, textHeight, axisPtr->theta,
  1930.         &bbWidth, &bbHeight, (XPoint *)NULL);
  1931.     }
  1932.     if (bbWidth > maxWidth) {
  1933.         maxWidth = bbWidth;
  1934.     }
  1935.     if (bbHeight > maxHeight) {
  1936.         maxHeight = bbHeight;
  1937.     }
  1938.     }
  1939.     if (axisPtr->labelArr != NULL) {
  1940.     free((char *)axisPtr->labelArr);
  1941.     }
  1942.     axisPtr->labelArr = labelArr;
  1943.     axisPtr->numLabels = count;
  1944.     assert(axisPtr->numLabels <= axisPtr->numTicks);
  1945.  
  1946.     /*
  1947.      * Because the axis cap style is "CapProjecting", there's an extra
  1948.      * 1.5 linewidth to be accounted for.
  1949.      */
  1950.     pad = ((axisPtr->lineWidth * 15) / 10) + 2;
  1951.     axisPtr->width = maxWidth + pad;
  1952.     axisPtr->height = maxHeight + pad;
  1953.  
  1954.     value = BLT_ABS(axisPtr->tickLength) * 1.4;
  1955.     pad = BLT_RND(value) + graphPtr->plotBW + 1;
  1956.     if (graphPtr->plotBW > 0) {
  1957.     pad++;
  1958.     }
  1959.     if (HORIZONTAL_AXIS(axisPtr)) {
  1960.     axisPtr->height += pad;
  1961.     } else {
  1962.     axisPtr->width += pad;
  1963.     }
  1964. }
  1965.  
  1966. void
  1967. Blt_UpdateAxisBackgrounds(graphPtr, colorPtr)
  1968.     Graph *graphPtr;
  1969.     XColor *colorPtr;        /* Background color of graph margin area */
  1970. {
  1971.     Axis *axisPtr;
  1972.     register int i;
  1973.  
  1974.     for (i = 0; i < 4; i++) {
  1975.     axisPtr = (Axis *)graphPtr->axisArr[i];
  1976.     XSetBackground(Tk_Display(graphPtr->tkwin), axisPtr->textGC,
  1977.         colorPtr->pixel);
  1978.     }
  1979. }
  1980.  
  1981. /*
  1982.  * -----------------------------------------------------------------
  1983.  *
  1984.  * Blt_ComputeLayout --
  1985.  *
  1986.  *     Calculate the layout of the graph.  Based upon the data,
  1987.  *    axis limits, X and Y titles, and title height, determine
  1988.  *    the cavity left which is the plotting surface.  The first
  1989.  *    step get the data and axis limits for calculating the space
  1990.  *    needed for the top, bottom, left, and right margins.
  1991.  *
  1992.  *     1) The LEFT margin is the area from the left border to the
  1993.  *       Y axis (not including ticks). It composes the border
  1994.  *       width, the width an optional Y axis label and its padding,
  1995.  *       and the tick numeric labels. The Y axis label is rotated
  1996.  *       90 degrees so that the width is the font height.
  1997.  *
  1998.  *     2) The RIGHT margin is the area from the end of the graph
  1999.  *       to the right window border. It composes the border width,
  2000.  *       some padding, the font height (this may be dubious. It
  2001.  *       appears to provide a more even border), the max of the
  2002.  *       legend width and 1/2 max X tick number. This last part is
  2003.  *       so that the last tick label is not clipped.
  2004.  *
  2005.  *           Area Width
  2006.  *      ___________________________________________________________
  2007.  *      |          |                               |               |
  2008.  *      |          |   TOP  height of title        |               |
  2009.  *      |          |                               |               |
  2010.  *      |          |           x2 title            |               |
  2011.  *      |          |                               |               |
  2012.  *      |          |        height of x2-axis      |               |
  2013.  *      |__________|_______________________________|_______________|  A
  2014.  *      |          |                        extreme|               |  r
  2015.  *      |          |                               |               |  e
  2016.  *      |   LEFT   |                               |     RIGHT     |  a
  2017.  *      |          |                               |               |
  2018.  *      | y        |     Free area = 104%          |      y2       |  H
  2019.  *      |          |     Plotting surface = 100%   |               |  e
  2020.  *      | t        |     Tick length = 2 + 2%      |      t        |  i
  2021.  *      | i        |                               |      i        |  g
  2022.  *      | t        |                               |      t  legend|  h
  2023.  *      | l        |                               |      l   width|  t
  2024.  *      | e        |                               |      e        |
  2025.  *      |    height|                               |height         |
  2026.  *      |       of |                               | of            |
  2027.  *      |    y-axis|                               |y2-axis        |
  2028.  *      |          |                               |               |
  2029.  *      |          |origin                         |               |
  2030.  *      |__________|_______________________________|_______________|
  2031.  *      |          | (xoffset, yoffset)            |               |
  2032.  *      |          |                               |               |
  2033.  *      |          |       height of x-axis        |               |
  2034.  *      |          |                               |               |
  2035.  *      |          |   BOTTOM   x title            |               |
  2036.  *      |__________|_______________________________|_______________|
  2037.  *
  2038.  * 3) The TOP margin is the area from the top window border to the top
  2039.  *    of the graph. It composes the border width, twice the height of
  2040.  *    the title font (if one is given) and some padding between the
  2041.  *    title.
  2042.  *
  2043.  * 4) The BOTTOM margin is area from the bottom window border to the
  2044.  *    X axis (not including ticks). It composes the border width, the height
  2045.  *    an optional X axis label and its padding, the height of the font
  2046.  *    of the tick labels.
  2047.  *
  2048.  * The plotting area is between the margins which includes the X and Y axes
  2049.  * including the ticks but not the tick numeric labels. The length of
  2050.  * the ticks and its padding is 5% of the entire plotting area.  Hence the
  2051.  * entire plotting area is scaled as 105% of the width and height of the
  2052.  * area.
  2053.  *
  2054.  * The axis labels, ticks labels, title, and legend may or may not be
  2055.  * displayed which must be taken into account.
  2056.  *
  2057.  *
  2058.  * -----------------------------------------------------------------
  2059.  */
  2060. int
  2061. Blt_ComputeLayout(graphPtr)
  2062.     Graph *graphPtr;
  2063. {
  2064.     int left, right, top, bottom;
  2065.     int maxTickWidth;
  2066.     int height;
  2067.     int leftOver;
  2068.     unsigned int borderWidths;
  2069.     unsigned int lineHeight = TEXTHEIGHT(graphPtr->fontPtr);
  2070.     Axis *x1, *x2, *y1, *y2;
  2071.     unsigned int twiceHeight = (2 * lineHeight);
  2072.     unsigned int halfHeight = (lineHeight / 2);
  2073.     double value;
  2074.  
  2075.     x1 = (Axis *)graphPtr->bottomAxis;
  2076.     x2 = (Axis *)graphPtr->topAxis;
  2077.     y1 = (Axis *)graphPtr->leftAxis;
  2078.     y2 = (Axis *)graphPtr->rightAxis;
  2079.  
  2080.     top = (graphPtr->title != NULL) ? twiceHeight : halfHeight;
  2081.     left = ((y1->mapped) && (y1->title != NULL)) ? twiceHeight : halfHeight;
  2082.     bottom = ((x1->mapped) && (x1->title != NULL)) ? twiceHeight : halfHeight;
  2083.     right = ((y2->mapped) && (y2->title != NULL)) ? twiceHeight : 0;
  2084.  
  2085.     if ((x2->mapped) && (x2->title != NULL)) {
  2086.     top += twiceHeight;
  2087.     }
  2088.     GetAxisGeometry(graphPtr, x1);
  2089.     maxTickWidth = 0;
  2090.     if ((x1->mapped) && (x1->showTicks)) {
  2091.     bottom += x1->height;
  2092.     }
  2093.     GetAxisGeometry(graphPtr, x2);
  2094.     if ((x2->mapped) && (x2->showTicks)) {
  2095.     top += x2->height;
  2096.     }
  2097.     GetAxisGeometry(graphPtr, y1);
  2098.     if ((y1->mapped) && (y1->showTicks)) {
  2099.     left += y1->width + PADX;
  2100.     }
  2101.     GetAxisGeometry(graphPtr, y2);
  2102.     if ((y2->mapped) && (y2->showTicks)) {
  2103.     right += y2->width + PADX;
  2104.     }
  2105.     /* Override calculated values if user specified margins */
  2106.  
  2107.     if (graphPtr->leftMargin > 0) {
  2108.     left = graphPtr->leftMargin;
  2109.     }
  2110.     if (graphPtr->topMargin > 0) {
  2111.     top = graphPtr->topMargin;
  2112.     }
  2113.     if (graphPtr->bottomMargin > 0) {
  2114.     bottom = graphPtr->bottomMargin;
  2115.     }
  2116.     borderWidths = graphPtr->borderWidth + graphPtr->plotBW;
  2117.     height = graphPtr->height - ((2 * borderWidths) + top + bottom);
  2118.     (*graphPtr->legendPtr->geomProc) (graphPtr, height);
  2119.     if ((graphPtr->legendPtr->mapped) && (graphPtr->legendPtr->useDefault)) {
  2120.     right += graphPtr->legendPtr->width;
  2121.     } else {
  2122.     right += halfHeight;
  2123.     }
  2124.     maxTickWidth = BLT_MAX(x1->width, x2->width) / 2;
  2125.     if (right < maxTickWidth) {
  2126.     right = maxTickWidth;
  2127.     }
  2128.     if (graphPtr->rightMargin > 0) {
  2129.     right = graphPtr->rightMargin;
  2130.     }
  2131.     top += borderWidths;
  2132.     left += borderWidths;
  2133.     right += borderWidths;
  2134.     bottom += borderWidths;
  2135.  
  2136.     /* Based upon the margins, calculate the space left for the graph. */
  2137.  
  2138.     x1->offset = left;
  2139.     y1->offset = graphPtr->height - bottom;
  2140.     leftOver = graphPtr->width - (left + right);
  2141.     if (leftOver < 0) {
  2142.     return TCL_ERROR;
  2143.     }
  2144.     x1->scale = (double)leftOver;    /* Pixels per X unit */
  2145.     leftOver = graphPtr->height - (top + bottom);
  2146.     if (leftOver < 0) {
  2147.     return TCL_ERROR;
  2148.     }
  2149.     y1->scale = (double)leftOver;    /* Pixels per Y unit */
  2150.     x2->scale = x1->scale;
  2151.     x2->offset = x1->offset;
  2152.     y2->scale = y1->scale;
  2153.     y2->offset = y1->offset;
  2154.  
  2155.     /* Calculate the average symbol (formula is arbitrary) */
  2156.  
  2157.     value = log(((double)x1->scale) * y1->scale) * 0.8;
  2158.     graphPtr->avgSymSize = BLT_RND(value);
  2159.  
  2160.     graphPtr->origin.x = MAPX(x1, 0.0);
  2161.     graphPtr->origin.y = MAPY(y1, 0.0);
  2162.     graphPtr->extreme.x = MAPX(x1, 1.0);
  2163.     graphPtr->extreme.y = MAPY(y1, 1.0);
  2164.     return TCL_OK;
  2165. }
  2166.  
  2167. /*
  2168.  *--------------------------------------------------------------
  2169.  *
  2170.  * GetAxisLimits --
  2171.  *
  2172.  *    This procedure returns a string representing the axis limits
  2173.  *    of the graph.  The format of the string is { xmin ymin xmax ymax}.
  2174.  *
  2175.  * Results:
  2176.  *    Always returns TCL_OK.  The interp->result field is
  2177.  *    a list of the graph axis limits.
  2178.  *
  2179.  *--------------------------------------------------------------
  2180.  */
  2181. static int
  2182. GetAxisLimits(axisPtr, argc, argv)
  2183.     Axis *axisPtr;
  2184.     int argc;
  2185.     char **argv;
  2186.  
  2187. {
  2188.     char string[TCL_DOUBLE_SPACE + 1];
  2189.     double min, max;
  2190.  
  2191.     if (argc != 3) {
  2192.     Tcl_AppendResult(axisPtr->interp, "wrong # args: should be \"",
  2193.         argv[0], " ", axisNames[axisPtr->type], "axis limits\"",
  2194.         (char *)NULL);
  2195.     return TCL_ERROR;
  2196.     }
  2197.     min = axisPtr->min, max = axisPtr->max;
  2198.     if (axisPtr->logScale) {
  2199.     min = BLT_EXP10(min);
  2200.     max = BLT_EXP10(max);
  2201.     }
  2202.     Tcl_PrintDouble(axisPtr->interp, min, string);
  2203.     Tcl_AppendElement(axisPtr->interp, string);
  2204.     Tcl_PrintDouble(axisPtr->interp, max, string);
  2205.     Tcl_AppendElement(axisPtr->interp, string);
  2206.     return TCL_OK;
  2207. }
  2208.  
  2209. /*
  2210.  * ----------------------------------------------------------------------
  2211.  *
  2212.  * InvTransformCoord --
  2213.  *
  2214.  *    Maps the given window coordinate into an axis-value.
  2215.  *
  2216.  * Results:
  2217.  *    Returns a standard Tcl result.  interp->result contains
  2218.  *    the axis value. If an error occurred, TCL_ERROR is returned
  2219.  *    and interp->result will contain an error message.
  2220.  *
  2221.  * ----------------------------------------------------------------------
  2222.  */
  2223. static int
  2224. InvTransformCoord(axisPtr, argc, argv)
  2225.     Axis *axisPtr;
  2226.     int argc;
  2227.     char **argv;
  2228. {
  2229.     int coord;            /* Integer window coordinate*/
  2230.     char string[TCL_DOUBLE_SPACE + 1];
  2231.     double value;
  2232.  
  2233.     if (argc != 4) {
  2234.     Tcl_AppendResult(axisPtr->interp, "wrong # args: should be \"",
  2235.         argv[0], " ", axisNames[axisPtr->type],
  2236.         "axis invtransform winPos\"", (char *)NULL);
  2237.     return TCL_ERROR;
  2238.     }
  2239.     if (Tcl_GetInt(axisPtr->interp, argv[2], &coord) != TCL_OK) {
  2240.     return TCL_ERROR;
  2241.     }
  2242.     value = Blt_InvTransform((GraphAxis *)axisPtr, coord);
  2243.     Tcl_PrintDouble(axisPtr->interp, value, string);
  2244.     Tcl_AppendElement(axisPtr->interp, string);
  2245.     return TCL_OK;
  2246. }
  2247.  
  2248. /*
  2249.  * ----------------------------------------------------------------------
  2250.  *
  2251.  * TransformCoord --
  2252.  *
  2253.  *    Maps the given axis-value to a window coordinate.
  2254.  *
  2255.  * Results:
  2256.  *    Returns a standard Tcl result.  interp->result contains
  2257.  *    the window coordinate. If an error occurred, TCL_ERROR
  2258.  *    is returned and interp->result will contain an error
  2259.  *    message.
  2260.  *
  2261.  * ----------------------------------------------------------------------
  2262.  */
  2263. static int
  2264. TransformCoord(axisPtr, argc, argv)
  2265.     Axis *axisPtr;        /* Axis */
  2266.     int argc;
  2267.     char **argv;
  2268. {
  2269.     double value;
  2270.     int coord;
  2271.  
  2272.     if (argc != 4) {
  2273.     Tcl_AppendResult(axisPtr->interp, "wrong # args: should be \"",
  2274.         argv[0], " ", axisNames[axisPtr->type], "axis transform value\"",
  2275.         (char *)NULL);
  2276.     return TCL_ERROR;
  2277.     }
  2278.     if (Tcl_ExprDouble(axisPtr->interp, argv[2], &value) != TCL_OK) {
  2279.     return TCL_ERROR;
  2280.     }
  2281.     coord = Blt_Transform((GraphAxis *)axisPtr, value);
  2282.     sprintf(axisPtr->interp->result, "%d", coord);
  2283.     return TCL_OK;
  2284. }
  2285.  
  2286. /*
  2287.  * ----------------------------------------------------------------------
  2288.  *
  2289.  * Blt_CreateAxis --
  2290.  *
  2291.  *    Create and initialize a structure containing information to
  2292.  *     display a graph axis.
  2293.  *
  2294.  * Results:
  2295.  *    The return value is a standard Tcl result.
  2296.  *
  2297.  * ----------------------------------------------------------------------
  2298.  */
  2299. int
  2300. Blt_CreateAxis(graphPtr, type, flags)
  2301.     Graph *graphPtr;
  2302.     enum AxisTypes type;
  2303.     int flags;            /* Configuration flags */
  2304. {
  2305.     Axis *axisPtr;
  2306.     enum AxisLocations location;
  2307.  
  2308.     axisPtr = (Axis *)calloc(1, sizeof(Axis));
  2309.     if (axisPtr == NULL) {
  2310.     graphPtr->interp->result = "can't allocate axis structure";
  2311.     return TCL_ERROR;
  2312.     }
  2313.     location = (AxisLocation) type;    /* For now, there's a 1-1
  2314.                      * correspondence between axis
  2315.                      * types and locations */
  2316.     axisPtr->type = type;
  2317.     axisPtr->location = location;
  2318.     axisPtr->interp = graphPtr->interp;    /* Needed for Tcl_PrintDouble */
  2319.     axisPtr->showTicks = 1;
  2320.     axisPtr->reqSubTicks = 2;
  2321.     axisPtr->destroyProc = DestroyAxis;
  2322.     axisPtr->displayProc = DisplayAxis;
  2323.     axisPtr->layoutProc = LayoutAxis;
  2324.     axisPtr->printProc = PrintAxis;
  2325.  
  2326.     /*
  2327.      * The actual axis min and max can't be the same, so initializing
  2328.      * the previous limits to zero is Ok.
  2329.      */
  2330.     axisPtr->prevMin = axisPtr->prevMax = 0.0;
  2331.     axisPtr->mapped = ((location == BOTTOM_AXIS) || (location == LEFT_AXIS));
  2332.  
  2333.     graphPtr->axisArr[type] = (GraphAxis *)axisPtr;
  2334.     if (ConfigureAxis(graphPtr, axisPtr, 0, (char **)NULL, flags) != TCL_OK) {
  2335.     return TCL_ERROR;
  2336.     }
  2337.     return TCL_OK;
  2338. }
  2339.  
  2340. int
  2341. Blt_AxisCmd(graphPtr, axis, argc, argv, flags)
  2342.     Graph *graphPtr;
  2343.     GraphAxis *axis;
  2344.     int argc;
  2345.     char **argv;
  2346.     int flags;
  2347. {
  2348.     int result = TCL_ERROR;
  2349.     Axis *axisPtr = (Axis *)axis;
  2350.     Tcl_Interp *interp = graphPtr->interp;
  2351.     char c;
  2352.     int length;
  2353.     char *which;
  2354.  
  2355.     which = axisNames[axisPtr->type];
  2356.     if (argc < 3) {
  2357.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  2358.         " ", which, "axis option ?args?\"", NULL);
  2359.     return TCL_ERROR;
  2360.     }
  2361.     c = argv[2][0];
  2362.     length = strlen(argv[2]);
  2363.  
  2364.     if ((c == 'c') && (strncmp(argv[2], "configure", length) == 0)) {
  2365.     if (argc < 3) {
  2366.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  2367.         " ", which, "axis configure ?args?\"", NULL);
  2368.         return TCL_ERROR;
  2369.     }
  2370.     result = ConfigureAxis(graphPtr, axisPtr, argc - 3, argv + 3, flags);
  2371.     } else if ((c == 'l') && (strncmp(argv[2], "limits", length) == 0)) {
  2372.     result = GetAxisLimits(axisPtr, argc, argv);
  2373.     } else if ((c == 'i') && (strncmp(argv[2], "invtransform", length) == 0)) {
  2374.     result = InvTransformCoord(axisPtr, argc, argv);
  2375.     } else if ((c == 't') && (strncmp(argv[2], "transform", length) == 0)) {
  2376.     result = TransformCoord(axisPtr, argc, argv);
  2377.     } else {
  2378.     Tcl_AppendResult(interp, "bad ", which, "axis option \"", argv[2],
  2379.         "\":  should be configure or limits", (char *)NULL);
  2380.     return TCL_ERROR;
  2381.     }
  2382.     return result;
  2383. }
  2384.